pax_global_header00006660000000000000000000000064130032775400014513gustar00rootroot0000000000000052 comment=9bcf5c521fae43b797efb8a65e490345999456f2 nemu-0.3.1/000077500000000000000000000000001300327754000124605ustar00rootroot00000000000000nemu-0.3.1/.gitignore000066400000000000000000000004731300327754000144540ustar00rootroot00000000000000*.py[cod] # C extensions *.so # Packages *.egg *.egg-info dist build eggs parts bin var sdist develop-eggs .installed.cfg lib lib64 __pycache__ # Installer logs pip-log.txt # Unit test / coverage reports .coverage .tox nosetests.xml # Translations *.mo # Mr Developer .mr.developer.cfg .project .pydevproject nemu-0.3.1/BUILDING.md000066400000000000000000000006251300327754000142020ustar00rootroot00000000000000# How to build nemu ## Dependencies * python-unshare (http://pypi.python.org/pypi/python-unshare) * python-passfd (http://pypi.python.org/pypi/python-passfd) * linux-kernel >= 2.6.35 * bridge-utils * iproute * procps * xauth (Needed only for X11 forwarding support) ## Details Once the dependencies are installed, run: $ make $ sudo make test # optional $ sudo make install nemu-0.3.1/COPYING000066400000000000000000000432541300327754000135230ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. nemu-0.3.1/MANIFEST000066400000000000000000000007071300327754000136150ustar00rootroot00000000000000BUILDING.md COPYING docs/protocol.txt docs/sample-api.txt docs/X11Forwarding.md examples/sample.py Makefile README.md setup.cfg setup.py src/nemu/environ.py src/nemu/__init__.py src/nemu/interface.py src/nemu/iproute.py src/nemu/node.py src/nemu/protocol.py src/nemu/subprocess_.py test/test_core.py test/test_interfaces.py test/test_node.py test/test_protocol.py test/test_routing.py test/test_subprocess.py test/test_switch.py test/test_util.py TODO.md nemu-0.3.1/Makefile000066400000000000000000000037161300327754000141270ustar00rootroot00000000000000SRC = src TEST = test BUILDDIR = build DISTDIR = dist COVERDIR = coverage SRCFILES := $(shell find $(SRC) -type f) # stupid distutils, it's broken in so many ways SUBBUILDDIR := $(shell python -c 'import distutils.util, sys; \ print "lib.%s-%s" % (distutils.util.get_platform(), \ sys.version[0:3])') PYTHON25 := $(shell python -c 'import sys; v = sys.version_info; \ print (1 if v[0] <= 2 and v[1] <= 5 else 0)') ifeq ($(PYTHON25),0) BUILDDIR := $(BUILDDIR)/$(SUBBUILDDIR) else BUILDDIR := $(BUILDDIR)/lib endif COVERAGE := $(or $(shell which coverage), $(shell which python-coverage), \ coverage) all: build_stamp build_stamp: $(SRCFILES) ./setup.py build touch $@ install: all ./setup.py install test: all retval=0; \ for i in `find "$(TEST)" -perm -u+x -type f`; do \ echo $$i; \ PYTHONPATH="$(BUILDDIR)" $$i || retval=$$?; \ done; exit $$retval coverage: coverage_stamp $(COVERAGE) -r -m `find "$(BUILDDIR)" -name \\*.py -type f` coverage-report: coverage_stamp rm -rf $(COVERDIR) $(COVERAGE) -b -d $(COVERDIR) `find "$(BUILDDIR)" -name \\*.py -type f` @echo "Coverage report created in $(COVERDIR)/index.html" coverage_stamp: build_stamp if [ `id -u` -ne 0 ]; then \ echo "Coverage needs to be run as root."; false; fi for i in `find "$(TEST)" -perm -u+x -type f`; do \ set -e; \ PYTHONPATH="$(BUILDDIR)" $(COVERAGE) -x $$i; \ done $(COVERAGE) -c touch $@ clean: ./setup.py clean rm -f `find -name \*.pyc` .coverage *.pcap *_stamp rm -rf $(COVERDIR) #$(MAKE) -C $(CURDIR)/benchmarks/ clean distclean: clean rm -rf "$(DISTDIR)" MANIFEST: distclean find . -path ./.git\* -prune -o -path ./build -prune -o \ -path ./docs/debconf-talk -prune -o \ -path ./benchmarks -prune -o \ -name \*.pyc -prune -o -name \*.swp -prune -o \ -name MANIFEST -prune -o -name .hg\* -prune -o \ -type f -print | sed 's#^\./##' | sort > MANIFEST dist: MANIFEST ./setup.py sdist .PHONY: clean distclean dist test coverage install MANIFEST nemu-0.3.1/README.md000066400000000000000000000037261300327754000137470ustar00rootroot00000000000000nemu ==== Nemu (Netwok EMUlator) is a small Python library to create emulated networks and run and test programs in them. Different programs, or copies of the same program, can run in different emulated nodes, using only the emulated network to communicate, without ever noticing they all run in the same computer. Nemu provides a very simple interface to create nodes, connect them arbitrarily with virtual interfaces, configure IPv4 and IPv6 addresses and routes, and start programs in the nodes. The virtual interfaces also support emulation of delays, loss, and reordering of packets, and bandwidth limitations. You can even start interactive sessions by opening xterms on different nodes, Nemu has special support for forwarding X sessions to the emulated nodes. More advanced configurations, like setting up netfilter (iptables) rules, starting VPN tunnels, routing daemons, etc, are simply supported by executing the appropriate commands in the emulated nodes, exactly as if they were executed in real machines in a real network. All this is achieved with very small overhead, thanks to the Linux kernel's [network name spaces][] capabilities, part of the bigger [Linux containers][] project. To get a feeling of what you can do with Nemu, take a peek at this [sample script](examples/sample.py) that creates 3 interconnected nodes, runs some tests and then starts xterms running tcpdump so you can see the packets flowing from one node to the other. Nemu was started as a research project at [INRIA][] (Institut de Recheche en Informatique et Automatique, a French research institution) and now it is developed jointly by INRIA staff and external developers. Nemu is now part of [Debian][]! You can just `apt-get install python-nemu` and start using it. [network name spaces]: http://lxc.sourceforge.net/index.php/about/kernel-namespaces/network/ [Linux containers]: http://lxc.sourceforge.net/ [INRIA]: http://www.inria.fr/en/ [Debian]: http://packages.qa.debian.org/p/python-nemu.html nemu-0.3.1/TODO.md000066400000000000000000000013021300327754000135430ustar00rootroot00000000000000In the years since I wrote this, docker and friends exploited namespaces to create much more complex frameworks than this. A good side effect is that iproute2 learnt to manage namespaces with some very useful commands. Also, a functioning python library to talk to NETLINK was published. Nemu would get a big performance and reliability improvement from adopting those: * [Python Netlink library](https://pypi.python.org/pypi/pyroute2) * [IProute2 namespace management](http://baturin.org/docs/iproute2/#Network%20namespace%20management) Another requested feature is to isolate the bridge interface from the default namespace, which was not possible a few years ago. I will have to research this again. nemu-0.3.1/benchmarks/000077500000000000000000000000001300327754000145755ustar00rootroot00000000000000nemu-0.3.1/benchmarks/Makefile000066400000000000000000000001261300327754000162340ustar00rootroot00000000000000CFLAGS = -Wall -g -O3 all: udp-perf clean: rm -f udp-perf *.pyc .PHONY: clean all nemu-0.3.1/benchmarks/graph.py000066400000000000000000000125661300327754000162620ustar00rootroot00000000000000# vim: ts=4:sw=4:et:ai:sts=4 import csv, StringIO, subprocess class Graph: [LINE, DOT, POINT, LINEPOINT] = range(0, 4) def __init__(self): self._plots = [] self._title = None def set_title(self, title): self._title = title def add(self, plot): self._plots.append(plot) def generate(self, output_file): lines = self.gen_output() lines.insert(0, "set terminal postscript") lines.insert(0, "set output '%s'" % filename) gnuplot = subprocess.Popen(['gnuplot', '-'], stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.STDOUT) gnuplot.communicate(input = "\n".join(lines)) def Xplot(self, plotnr): lines = self.gen_output(plotnr) lines.insert(0, "set terminal wxt") lines.append('pause mouse') gnuplot = subprocess.Popen(['gnuplot', '-'], stdin = subprocess.PIPE) gnuplot.communicate(input = "\n".join(lines)) def _style_to_str(self, style): d = {Graph.LINE: 'lines', Graph.DOT: 'dots', Graph.POINT: 'points', Graph.LINEPOINT: 'linespoints'} return d[style] def gen_output(self, plots = None): if plots: plots = map(self._plots.__getitem__, plots) else: plots = self._plots lines = [] if self._title: lines.append("set title '%s'" % self._title) line = [] for plot in plots: line.append("'-' title '%s' with %s" % (plot.title(), self._style_to_str(plot.style()))) lines.append('plot ' + ', '.join(line)) for plot in plots: for r in plot.data(): r = [str(d) for d in r] lines.append(' '.join(r)) lines.extend(['e']) return lines class Plot: def __init__(self, title, data, style = Graph.LINE): self._title = title self._data = data self._style = style def style(self): return self._style def title(self): return self._title def data(self): return self._data class Row: def __init__(self, data, names = None): assert not names or len(names) == len(data) assert not names or all(map(lambda x: isinstance(x, str), names)) self._data1 = list(data) if names: self._data2 = dict(zip(names, data)) else: self._data2 = dict() def append(self, value, name = None): self._data1.append(value) if self._data2: assert name not in self._data2 self._data2[name] = value def __getitem__(self, item): if isinstance(item, int): return self._data1[item] else: return self._data2[item] def __len__(self): return len(self._data1) # def __repr__(self): # return class Data: def __init__(self, rows = [], colnames = []): assert not (colnames and rows) or len(colnames) == len(rows[0]) self._colnames = colnames self._data = [] for r in rows: self.add_row(r) def add_row(self, row): if isinstance(row, Row): self._data.append(row) else: self._data.append(Row(row, self._colnames)) def nrows(self): return len(self._data) def ncols(self): return len(self._data[0]) def read_csv(self, stream, has_header = False): self._data = [] self._colnames = [] self._datadict = [] n = 0 reader = csv.reader(stream) for line in reader: if n and len(line) != n: raise 'Not matching number of columns in different rows' if not n: n = len(line) if has_header: self._colnames = line continue row = [] for i in line: try: row.append(float(i)) except: row.append(i) self._data.append(row) if has_header: self._gen_data_dict() def write_csv(self, stream): writer = csv.writer(stream) writer.writerows(self._data) def column(self, col): if isinstance(col, int): return [row[col] for row in self._data] else: return [row[col] for row in self._datadict] def row(self, row): return self._data[row] def cell(self, row, col): if isinstance(col, int): return self._data[row][col] else: return self._datadict[row][col] def select(self, cols = None, selectfn = lambda x: True, groupfn = lambda x: None): if cols: cols = list(cols) else: cols = range(self.ncols()) groups = {} res = [] for row in self._data if isinstance(cols[0], int) else self._datadict: if not selectfn(row): continue def add_column(self, fn, colname = None): if self._colnames: assert colname for row in self._data: row.append(fn(row)) if colname: self._colname.append(colname) for row in self._datadict: row[colname] = fn(row) return self.ncols() - 1 def uniq(l): data = [] for i in l: if i not in data: data.append(i) return data nemu-0.3.1/benchmarks/linear-raw-throughput.py000077500000000000000000000202771300327754000214320ustar00rootroot00000000000000#!/usr/bin/env python # vim: ts=4:sw=4:et:ai:sts=4 import csv, getopt, nemu, os, os.path, re, select, subprocess, sys __doc__ = """Creates a linear network topology, and measures the maximum end-to-end throughput for the specified packet size.""" def usage(f): f.write("Usage: %s --nodes=NUM [TOPOLOGY_OPTIONS] [TEST_OPTIONS]\n%s\n\n" % (os.path.basename(sys.argv[0]), __doc__)) f.write("Topology configuration:\n") f.write(" -n, --nodes=NUM Number of nodes to create (mandatory)\n") f.write(" --use-p2p Use P2P links, to avoid bridging\n") f.write(" --delay=SECS Add delay emulation in links\n") f.write(" --jitter=PERCENT Add jitter emulation in links\n") f.write(" --bandwidth=BPS Maximum bandwidth of links\n\n") f.write("Test specification:\n") f.write(" Parameters take single values or ranges of falues in the form " + "nnn-NNN, a test\n") f.write(" will be run for each possible combination.\n") f.write(" -s, --pktsize=BYTES Size of packet payload (mandatory)\n") f.write(" --bwlimit=BPS Apply bandwidth limitation in the " + "traffic generator\n\n") # background noise f.write("How long should each test run (defaults to -t 10):\n") f.write(" -t, --time=SECS Stop after SECS seconds\n") f.write(" -p, --packets=NUM Stop after NUM packets\n") f.write(" -b, --bytes=BYTES Stop after BYTES bytes sent\n\n") f.write("Output format:\n") f.write(" --format=FMT Valid values are `csv', `brief', " + "and `verbose'\n") def main(): error = None opts = [] try: opts, args = getopt.getopt(sys.argv[1:], "hn:s:t:p:b:", [ "help", "nodes=", "pktsize=", "time=", "packets=", "bytes=", "use-p2p", "delay=", "jitter=", "bandwidth=", "format=" ]) except getopt.GetoptError, err: error = str(err) # opts will be empty pktsize = nr = time = packets = nbytes = None delay = jitter = bandwidth = None use_p2p = False format = "verbose" for o, a in opts: if o in ("-h", "--help"): usage(sys.stdout) sys.exit(0) elif o in ("-n", "--nodes"): nr = int(a) if nr > 65: error = "Invalid value for %s: %s" % (o, a) break elif o in ("-s", "--pktsize"): pktsize = int(a) elif o in ("-t", "--time"): time = float(a) elif o in ("-p", "--packets"): packets = int(a) elif o in ("--bytes"): nbytes = int(a) elif o in ("--delay"): delay = float(a) elif o in ("--jitter"): jitter = float(a) elif o in ("--bandwidth"): bandwidth = float(a) elif o in ("--use-p2p"): use_p2p = True continue # avoid the value check elif o == "--format": if a not in ('csv', 'brief', 'verbose'): error = "Invalid value for %s: %s" % (o, a) break format = a continue else: raise RuntimeError("Cannot happen") # In all cases, I take a number if float(a) <= 0: error = "Invalid value for %s: %s" % (o, a) if not error: if args: error = "Unknown argument(s): %s" % " ".join(args) elif not nr: error = "Missing mandatory --nodes argument" elif not pktsize: error = "Missing mandatory --pktsize argument" elif use_p2p and (delay or jitter or bandwidth): error = "Cannot use links emulation with P2P links" if error: sys.stderr.write("%s: %s\n" % (os.path.basename(sys.argv[0]), error)) sys.stderr.write("Try `%s --help' for more information.\n" % os.path.basename(sys.argv[0])) #usage(sys.stderr) sys.exit(2) if not (time or nbytes or packets): time = 10 udp_perf = nemu.environ.find_bin("udp-perf", extra_path = [".", os.path.dirname(sys.argv[0])]) if not udp_perf: raise RuntimeError("Cannot find `udp-perf'") nodes, interfaces, links = create_topo(nr, use_p2p, delay, jitter, bandwidth) cmdline = [udp_perf, "--server"] if time: cmdline.append("--max-time=%d" % time) if packets: cmdline.append("--max-pkts=%d" % packets) if nbytes: cmdline.append("--max-bytes=%d" % nbytes) if format == "verbose": cmdline.append("--verbose") srv = nodes[0].Popen(cmdline, stdout = subprocess.PIPE) cmdline = [udp_perf, "--client", "--pktsize=%d" % pktsize] if nr > 1: cmdline.append("--host=10.0.0.1") clt = nodes[nr - 1].Popen(cmdline) out = "" while(True): ready = select.select([srv.stdout], [], [], 0.1)[0] if ready: r = os.read(srv.stdout.fileno(), 1024) if not r: break out += r if srv.poll() != None or clt.poll() != None: break if srv.poll(): raise RuntimeError("upd-perf server returned with error.") if clt.poll(): raise RuntimeError("upd-perf client returned with error.") srv.wait() clt.wait() out = out.strip() if format != "csv": print "Command line: %s" % (" ".join(sys.argv[1:])) print out.strip() return data = out.split(" ") data = dict(map(lambda s: s.partition(":")[::2], data)) if sorted(data.keys()) != sorted(["brx", "prx", "pksz", "plsz", "err", "mind", "avgd", "maxd", "jit", "time"]): raise RuntimeError("Invalid output from udp-perf") data["nodes"] = nr data["bridge"] = int(not use_p2p) data["cfg_dly"] = delay if delay else "" data["cfg_bw"] = bandwidth if bandwidth else "" data["cfg_jit"] = jitter if jitter else "" res = [] for i in ["nodes", "bridge", "cfg_dly", "cfg_bw", "cfg_jit", "brx", "prx", "pksz", "plsz", "err", "mind", "avgd", "maxd", "jit", "time"]: res.append(data[i]) writer = csv.writer(sys.stdout) writer.writerow(res) def ip2dec(ip): match = re.search(r'^(\d+)\.(\d+)\.(\d+)\.(\d+)$', ip) assert match return long(match.group(1)) * 2**24 + long(match.group(2)) * 2**16 + \ long(match.group(3)) * 2**8 + long(match.group(4)) def dec2ip(dec): res = [None] * 4 for i in range(4): res[3 - i] = dec % 256 dec >>= 8 return "%d.%d.%d.%d" % tuple(res) def create_topo(n, p2p, delay, jitter, bw): nodes = [] interfaces = [] links = [] for i in range(n): nodes.append(nemu.Node()) if p2p: interfaces = [[None]] for i in range(n - 1): a, b = nemu.P2PInterface.create_pair(nodes[i], nodes[i + 1]) interfaces[i].append(a) interfaces.append([]) interfaces[i + 1] = [b] interfaces[n - 1].append(None) else: for i in range(n): if i > 0: left = nodes[i].add_if() else: left = None if i < n - 1: right = nodes[i].add_if() else: right = None interfaces.append((left, right)) for i in range(n - 1): links = nemu.Switch(bandwidth = bw, delay = delay, delay_jitter = jitter) links.up = True links.connect(interfaces[i][1]) links.connect(interfaces[i + 1][0]) links.append(links) for i in range(n): for j in (0, 1): if interfaces[i][j]: interfaces[i][j].up = True ip = ip2dec("10.0.0.1") for i in range(n - 1): interfaces[i][1].add_v4_address(dec2ip(ip), 30) interfaces[i + 1][0].add_v4_address(dec2ip(ip + 1), 30) ip += 4 ipbase = ip2dec("10.0.0.0") lastnet = dec2ip(ipbase + 4 * (n - 2)) for i in range(n - 2): nodes[i].add_route(prefix = lastnet, prefix_len = 30, nexthop = dec2ip(ipbase + 4 * i + 2)) nodes[n - 1 - i].add_route(prefix = "10.0.0.0", prefix_len = 30, nexthop = dec2ip(ipbase + (n - 2 - i) * 4 + 1)) return nodes, interfaces, links if __name__ == "__main__": main() nemu-0.3.1/benchmarks/preliminar/000077500000000000000000000000001300327754000167375ustar00rootroot00000000000000nemu-0.3.1/benchmarks/preliminar/comparison-bynodes.gnuplot000066400000000000000000000012561300327754000241700ustar00rootroot00000000000000set terminal postscript colour enhanced landscape lw 1 10 set key box left top width 1 title 'Experiment' set xlabel 'Number of nodes' set ylabel 'Processing cost per packet (10E-6 sec)' set title 'Processing cost for 1000-byte packets' set xrange [0:35] plot \ 'results-simu.txt' index 0 every 8::1 using 2:3 title "Exp 1" with linespoints, \ 'results-simu.txt' index 1 every 8::1 using 2:3 title "Exp 2" with linespoints, \ 'results-simu.txt' index 2 every 8::1 using 2:3 title "Exp 3" with linespoints, \ 'results.txt' index 0 every 13::11 using 1:($10/$3) title "Exp 4" with linespoints, \ 'results.txt' index 1 every 13::11 using 1:($10/$3) title "Exp 4bis" with linespoints nemu-0.3.1/benchmarks/preliminar/comparison-bynodes.ps000066400000000000000000000507511300327754000231260ustar00rootroot00000000000000%!PS-Adobe-2.0 %%Creator: gnuplot 4.2 patchlevel 3 %%CreationDate: Mon Aug 16 13:02:27 2010 %%DocumentFonts: (atend) %%BoundingBox: 50 50 554 770 %%Orientation: Landscape %%Pages: (atend) %%EndComments %%BeginProlog /gnudict 256 dict def gnudict begin % % The following 6 true/false flags may be edited by hand if required % The unit line width may also be changed % /Color true def /Blacktext false def /Solid false def /Dashlength 1 def /Landscape true def /Level1 false def /Rounded false def /TransparentPatterns false def /gnulinewidth 5.000 def /userlinewidth gnulinewidth def % /vshift -33 def /dl1 { 10.0 Dashlength mul mul Rounded { currentlinewidth 0.75 mul sub dup 0 le { pop 0.01 } if } if } def /dl2 { 10.0 Dashlength mul mul Rounded { currentlinewidth 0.75 mul add } if } def /hpt_ 31.5 def /vpt_ 31.5 def /hpt hpt_ def /vpt vpt_ def Level1 {} { /SDict 10 dict def systemdict /pdfmark known not { userdict /pdfmark systemdict /cleartomark get put } if SDict begin [ /Title () /Subject (gnuplot plot) /Creator (gnuplot 4.2 patchlevel 3 ) /Author (Martin_Hernan Ferrari,L133,2417) % /Producer (gnuplot) % /Keywords () /CreationDate (Mon Aug 16 13:02:27 2010) /DOCINFO pdfmark end } ifelse % % Gnuplot Prolog Version 4.2 (August 2006) % /M {moveto} bind def /L {lineto} bind def /R {rmoveto} bind def /V {rlineto} bind def /N {newpath moveto} bind def /Z {closepath} bind def /C {setrgbcolor} bind def /f {rlineto fill} bind def /vpt2 vpt 2 mul def /hpt2 hpt 2 mul def /Lshow {currentpoint stroke M 0 vshift R Blacktext {gsave 0 setgray show grestore} {show} ifelse} def /Rshow {currentpoint stroke M dup stringwidth pop neg vshift R Blacktext {gsave 0 setgray show grestore} {show} ifelse} def /Cshow {currentpoint stroke M dup stringwidth pop -2 div vshift R Blacktext {gsave 0 setgray show grestore} {show} ifelse} def /UP {dup vpt_ mul /vpt exch def hpt_ mul /hpt exch def /hpt2 hpt 2 mul def /vpt2 vpt 2 mul def} def /DL {Color {setrgbcolor Solid {pop []} if 0 setdash} {pop pop pop 0 setgray Solid {pop []} if 0 setdash} ifelse} def /BL {stroke userlinewidth 2 mul setlinewidth Rounded {1 setlinejoin 1 setlinecap} if} def /AL {stroke userlinewidth 2 div setlinewidth Rounded {1 setlinejoin 1 setlinecap} if} def /UL {dup gnulinewidth mul /userlinewidth exch def dup 1 lt {pop 1} if 10 mul /udl exch def} def /PL {stroke userlinewidth setlinewidth Rounded {1 setlinejoin 1 setlinecap} if} def % Default Line colors /LCw {1 1 1} def /LCb {0 0 0} def /LCa {0 0 0} def /LC0 {1 0 0} def /LC1 {0 1 0} def /LC2 {0 0 1} def /LC3 {1 0 1} def /LC4 {0 1 1} def /LC5 {1 1 0} def /LC6 {0 0 0} def /LC7 {1 0.3 0} def /LC8 {0.5 0.5 0.5} def % Default Line Types /LTw {PL [] 1 setgray} def /LTb {BL [] LCb DL} def /LTa {AL [1 udl mul 2 udl mul] 0 setdash LCa setrgbcolor} def /LT0 {PL [] LC0 DL} def /LT1 {PL [4 dl1 2 dl2] LC1 DL} def /LT2 {PL [2 dl1 3 dl2] LC2 DL} def /LT3 {PL [1 dl1 1.5 dl2] LC3 DL} def /LT4 {PL [6 dl1 2 dl2 1 dl1 2 dl2] LC4 DL} def /LT5 {PL [3 dl1 3 dl2 1 dl1 3 dl2] LC5 DL} def /LT6 {PL [2 dl1 2 dl2 2 dl1 6 dl2] LC6 DL} def /LT7 {PL [1 dl1 2 dl2 6 dl1 2 dl2 1 dl1 2 dl2] LC7 DL} def /LT8 {PL [2 dl1 2 dl2 2 dl1 2 dl2 2 dl1 2 dl2 2 dl1 4 dl2] LC8 DL} def /Pnt {stroke [] 0 setdash gsave 1 setlinecap M 0 0 V stroke grestore} def /Dia {stroke [] 0 setdash 2 copy vpt add M hpt neg vpt neg V hpt vpt neg V hpt vpt V hpt neg vpt V closepath stroke Pnt} def /Pls {stroke [] 0 setdash vpt sub M 0 vpt2 V currentpoint stroke M hpt neg vpt neg R hpt2 0 V stroke } def /Box {stroke [] 0 setdash 2 copy exch hpt sub exch vpt add M 0 vpt2 neg V hpt2 0 V 0 vpt2 V hpt2 neg 0 V closepath stroke Pnt} def /Crs {stroke [] 0 setdash exch hpt sub exch vpt add M hpt2 vpt2 neg V currentpoint stroke M hpt2 neg 0 R hpt2 vpt2 V stroke} def /TriU {stroke [] 0 setdash 2 copy vpt 1.12 mul add M hpt neg vpt -1.62 mul V hpt 2 mul 0 V hpt neg vpt 1.62 mul V closepath stroke Pnt} def /Star {2 copy Pls Crs} def /BoxF {stroke [] 0 setdash exch hpt sub exch vpt add M 0 vpt2 neg V hpt2 0 V 0 vpt2 V hpt2 neg 0 V closepath fill} def /TriUF {stroke [] 0 setdash vpt 1.12 mul add M hpt neg vpt -1.62 mul V hpt 2 mul 0 V hpt neg vpt 1.62 mul V closepath fill} def /TriD {stroke [] 0 setdash 2 copy vpt 1.12 mul sub M hpt neg vpt 1.62 mul V hpt 2 mul 0 V hpt neg vpt -1.62 mul V closepath stroke Pnt} def /TriDF {stroke [] 0 setdash vpt 1.12 mul sub M hpt neg vpt 1.62 mul V hpt 2 mul 0 V hpt neg vpt -1.62 mul V closepath fill} def /DiaF {stroke [] 0 setdash vpt add M hpt neg vpt neg V hpt vpt neg V hpt vpt V hpt neg vpt V closepath fill} def /Pent {stroke [] 0 setdash 2 copy gsave translate 0 hpt M 4 {72 rotate 0 hpt L} repeat closepath stroke grestore Pnt} def /PentF {stroke [] 0 setdash gsave translate 0 hpt M 4 {72 rotate 0 hpt L} repeat closepath fill grestore} def /Circle {stroke [] 0 setdash 2 copy hpt 0 360 arc stroke Pnt} def /CircleF {stroke [] 0 setdash hpt 0 360 arc fill} def /C0 {BL [] 0 setdash 2 copy moveto vpt 90 450 arc} bind def /C1 {BL [] 0 setdash 2 copy moveto 2 copy vpt 0 90 arc closepath fill vpt 0 360 arc closepath} bind def /C2 {BL [] 0 setdash 2 copy moveto 2 copy vpt 90 180 arc closepath fill vpt 0 360 arc closepath} bind def /C3 {BL [] 0 setdash 2 copy moveto 2 copy vpt 0 180 arc closepath fill vpt 0 360 arc closepath} bind def /C4 {BL [] 0 setdash 2 copy moveto 2 copy vpt 180 270 arc closepath fill vpt 0 360 arc closepath} bind def /C5 {BL [] 0 setdash 2 copy moveto 2 copy vpt 0 90 arc 2 copy moveto 2 copy vpt 180 270 arc closepath fill vpt 0 360 arc} bind def /C6 {BL [] 0 setdash 2 copy moveto 2 copy vpt 90 270 arc closepath fill vpt 0 360 arc closepath} bind def /C7 {BL [] 0 setdash 2 copy moveto 2 copy vpt 0 270 arc closepath fill vpt 0 360 arc closepath} bind def /C8 {BL [] 0 setdash 2 copy moveto 2 copy vpt 270 360 arc closepath fill vpt 0 360 arc closepath} bind def /C9 {BL [] 0 setdash 2 copy moveto 2 copy vpt 270 450 arc closepath fill vpt 0 360 arc closepath} bind def /C10 {BL [] 0 setdash 2 copy 2 copy moveto vpt 270 360 arc closepath fill 2 copy moveto 2 copy vpt 90 180 arc closepath fill vpt 0 360 arc closepath} bind def /C11 {BL [] 0 setdash 2 copy moveto 2 copy vpt 0 180 arc closepath fill 2 copy moveto 2 copy vpt 270 360 arc closepath fill vpt 0 360 arc closepath} bind def /C12 {BL [] 0 setdash 2 copy moveto 2 copy vpt 180 360 arc closepath fill vpt 0 360 arc closepath} bind def /C13 {BL [] 0 setdash 2 copy moveto 2 copy vpt 0 90 arc closepath fill 2 copy moveto 2 copy vpt 180 360 arc closepath fill vpt 0 360 arc closepath} bind def /C14 {BL [] 0 setdash 2 copy moveto 2 copy vpt 90 360 arc closepath fill vpt 0 360 arc} bind def /C15 {BL [] 0 setdash 2 copy vpt 0 360 arc closepath fill vpt 0 360 arc closepath} bind def /Rec {newpath 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto neg 0 rlineto closepath} bind def /Square {dup Rec} bind def /Bsquare {vpt sub exch vpt sub exch vpt2 Square} bind def /S0 {BL [] 0 setdash 2 copy moveto 0 vpt rlineto BL Bsquare} bind def /S1 {BL [] 0 setdash 2 copy vpt Square fill Bsquare} bind def /S2 {BL [] 0 setdash 2 copy exch vpt sub exch vpt Square fill Bsquare} bind def /S3 {BL [] 0 setdash 2 copy exch vpt sub exch vpt2 vpt Rec fill Bsquare} bind def /S4 {BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt Square fill Bsquare} bind def /S5 {BL [] 0 setdash 2 copy 2 copy vpt Square fill exch vpt sub exch vpt sub vpt Square fill Bsquare} bind def /S6 {BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt vpt2 Rec fill Bsquare} bind def /S7 {BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt vpt2 Rec fill 2 copy vpt Square fill Bsquare} bind def /S8 {BL [] 0 setdash 2 copy vpt sub vpt Square fill Bsquare} bind def /S9 {BL [] 0 setdash 2 copy vpt sub vpt vpt2 Rec fill Bsquare} bind def /S10 {BL [] 0 setdash 2 copy vpt sub vpt Square fill 2 copy exch vpt sub exch vpt Square fill Bsquare} bind def /S11 {BL [] 0 setdash 2 copy vpt sub vpt Square fill 2 copy exch vpt sub exch vpt2 vpt Rec fill Bsquare} bind def /S12 {BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt2 vpt Rec fill Bsquare} bind def /S13 {BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt2 vpt Rec fill 2 copy vpt Square fill Bsquare} bind def /S14 {BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt2 vpt Rec fill 2 copy exch vpt sub exch vpt Square fill Bsquare} bind def /S15 {BL [] 0 setdash 2 copy Bsquare fill Bsquare} bind def /D0 {gsave translate 45 rotate 0 0 S0 stroke grestore} bind def /D1 {gsave translate 45 rotate 0 0 S1 stroke grestore} bind def /D2 {gsave translate 45 rotate 0 0 S2 stroke grestore} bind def /D3 {gsave translate 45 rotate 0 0 S3 stroke grestore} bind def /D4 {gsave translate 45 rotate 0 0 S4 stroke grestore} bind def /D5 {gsave translate 45 rotate 0 0 S5 stroke grestore} bind def /D6 {gsave translate 45 rotate 0 0 S6 stroke grestore} bind def /D7 {gsave translate 45 rotate 0 0 S7 stroke grestore} bind def /D8 {gsave translate 45 rotate 0 0 S8 stroke grestore} bind def /D9 {gsave translate 45 rotate 0 0 S9 stroke grestore} bind def /D10 {gsave translate 45 rotate 0 0 S10 stroke grestore} bind def /D11 {gsave translate 45 rotate 0 0 S11 stroke grestore} bind def /D12 {gsave translate 45 rotate 0 0 S12 stroke grestore} bind def /D13 {gsave translate 45 rotate 0 0 S13 stroke grestore} bind def /D14 {gsave translate 45 rotate 0 0 S14 stroke grestore} bind def /D15 {gsave translate 45 rotate 0 0 S15 stroke grestore} bind def /DiaE {stroke [] 0 setdash vpt add M hpt neg vpt neg V hpt vpt neg V hpt vpt V hpt neg vpt V closepath stroke} def /BoxE {stroke [] 0 setdash exch hpt sub exch vpt add M 0 vpt2 neg V hpt2 0 V 0 vpt2 V hpt2 neg 0 V closepath stroke} def /TriUE {stroke [] 0 setdash vpt 1.12 mul add M hpt neg vpt -1.62 mul V hpt 2 mul 0 V hpt neg vpt 1.62 mul V closepath stroke} def /TriDE {stroke [] 0 setdash vpt 1.12 mul sub M hpt neg vpt 1.62 mul V hpt 2 mul 0 V hpt neg vpt -1.62 mul V closepath stroke} def /PentE {stroke [] 0 setdash gsave translate 0 hpt M 4 {72 rotate 0 hpt L} repeat closepath stroke grestore} def /CircE {stroke [] 0 setdash hpt 0 360 arc stroke} def /Opaque {gsave closepath 1 setgray fill grestore 0 setgray closepath} def /DiaW {stroke [] 0 setdash vpt add M hpt neg vpt neg V hpt vpt neg V hpt vpt V hpt neg vpt V Opaque stroke} def /BoxW {stroke [] 0 setdash exch hpt sub exch vpt add M 0 vpt2 neg V hpt2 0 V 0 vpt2 V hpt2 neg 0 V Opaque stroke} def /TriUW {stroke [] 0 setdash vpt 1.12 mul add M hpt neg vpt -1.62 mul V hpt 2 mul 0 V hpt neg vpt 1.62 mul V Opaque stroke} def /TriDW {stroke [] 0 setdash vpt 1.12 mul sub M hpt neg vpt 1.62 mul V hpt 2 mul 0 V hpt neg vpt -1.62 mul V Opaque stroke} def /PentW {stroke [] 0 setdash gsave translate 0 hpt M 4 {72 rotate 0 hpt L} repeat Opaque stroke grestore} def /CircW {stroke [] 0 setdash hpt 0 360 arc Opaque stroke} def /BoxFill {gsave Rec 1 setgray fill grestore} def /Density { /Fillden exch def currentrgbcolor /ColB exch def /ColG exch def /ColR exch def /ColR ColR Fillden mul Fillden sub 1 add def /ColG ColG Fillden mul Fillden sub 1 add def /ColB ColB Fillden mul Fillden sub 1 add def ColR ColG ColB setrgbcolor} def /BoxColFill {gsave Rec PolyFill} def /PolyFill {gsave Density fill grestore grestore} def /h {rlineto rlineto rlineto gsave closepath fill grestore} bind def % % PostScript Level 1 Pattern Fill routine for rectangles % Usage: x y w h s a XX PatternFill % x,y = lower left corner of box to be filled % w,h = width and height of box % a = angle in degrees between lines and x-axis % XX = 0/1 for no/yes cross-hatch % /PatternFill {gsave /PFa [ 9 2 roll ] def PFa 0 get PFa 2 get 2 div add PFa 1 get PFa 3 get 2 div add translate PFa 2 get -2 div PFa 3 get -2 div PFa 2 get PFa 3 get Rec gsave 1 setgray fill grestore clip currentlinewidth 0.5 mul setlinewidth /PFs PFa 2 get dup mul PFa 3 get dup mul add sqrt def 0 0 M PFa 5 get rotate PFs -2 div dup translate 0 1 PFs PFa 4 get div 1 add floor cvi {PFa 4 get mul 0 M 0 PFs V} for 0 PFa 6 get ne { 0 1 PFs PFa 4 get div 1 add floor cvi {PFa 4 get mul 0 2 1 roll M PFs 0 V} for } if stroke grestore} def % /languagelevel where {pop languagelevel} {1} ifelse 2 lt {/InterpretLevel1 true def} {/InterpretLevel1 Level1 def} ifelse % % PostScript level 2 pattern fill definitions % /Level2PatternFill { /Tile8x8 {/PaintType 2 /PatternType 1 /TilingType 1 /BBox [0 0 8 8] /XStep 8 /YStep 8} bind def /KeepColor {currentrgbcolor [/Pattern /DeviceRGB] setcolorspace} bind def << Tile8x8 /PaintProc {0.5 setlinewidth pop 0 0 M 8 8 L 0 8 M 8 0 L stroke} >> matrix makepattern /Pat1 exch def << Tile8x8 /PaintProc {0.5 setlinewidth pop 0 0 M 8 8 L 0 8 M 8 0 L stroke 0 4 M 4 8 L 8 4 L 4 0 L 0 4 L stroke} >> matrix makepattern /Pat2 exch def << Tile8x8 /PaintProc {0.5 setlinewidth pop 0 0 M 0 8 L 8 8 L 8 0 L 0 0 L fill} >> matrix makepattern /Pat3 exch def << Tile8x8 /PaintProc {0.5 setlinewidth pop -4 8 M 8 -4 L 0 12 M 12 0 L stroke} >> matrix makepattern /Pat4 exch def << Tile8x8 /PaintProc {0.5 setlinewidth pop -4 0 M 8 12 L 0 -4 M 12 8 L stroke} >> matrix makepattern /Pat5 exch def << Tile8x8 /PaintProc {0.5 setlinewidth pop -2 8 M 4 -4 L 0 12 M 8 -4 L 4 12 M 10 0 L stroke} >> matrix makepattern /Pat6 exch def << Tile8x8 /PaintProc {0.5 setlinewidth pop -2 0 M 4 12 L 0 -4 M 8 12 L 4 -4 M 10 8 L stroke} >> matrix makepattern /Pat7 exch def << Tile8x8 /PaintProc {0.5 setlinewidth pop 8 -2 M -4 4 L 12 0 M -4 8 L 12 4 M 0 10 L stroke} >> matrix makepattern /Pat8 exch def << Tile8x8 /PaintProc {0.5 setlinewidth pop 0 -2 M 12 4 L -4 0 M 12 8 L -4 4 M 8 10 L stroke} >> matrix makepattern /Pat9 exch def /Pattern1 {PatternBgnd KeepColor Pat1 setpattern} bind def /Pattern2 {PatternBgnd KeepColor Pat2 setpattern} bind def /Pattern3 {PatternBgnd KeepColor Pat3 setpattern} bind def /Pattern4 {PatternBgnd KeepColor Landscape {Pat5} {Pat4} ifelse setpattern} bind def /Pattern5 {PatternBgnd KeepColor Landscape {Pat4} {Pat5} ifelse setpattern} bind def /Pattern6 {PatternBgnd KeepColor Landscape {Pat9} {Pat6} ifelse setpattern} bind def /Pattern7 {PatternBgnd KeepColor Landscape {Pat8} {Pat7} ifelse setpattern} bind def } def % % %End of PostScript Level 2 code % /PatternBgnd { TransparentPatterns {} {gsave 1 setgray fill grestore} ifelse } def % % Substitute for Level 2 pattern fill codes with % grayscale if Level 2 support is not selected. % /Level1PatternFill { /Pattern1 {0.250 Density} bind def /Pattern2 {0.500 Density} bind def /Pattern3 {0.750 Density} bind def /Pattern4 {0.125 Density} bind def /Pattern5 {0.375 Density} bind def /Pattern6 {0.625 Density} bind def /Pattern7 {0.875 Density} bind def } def % % Now test for support of Level 2 code % Level1 {Level1PatternFill} {Level2PatternFill} ifelse % /Symbol-Oblique /Symbol findfont [1 0 .167 1 0 0] makefont dup length dict begin {1 index /FID eq {pop pop} {def} ifelse} forall currentdict end definefont pop /MFshow { { dup 5 get 3 ge { 5 get 3 eq {gsave} {grestore} ifelse } {dup dup 0 get findfont exch 1 get scalefont setfont [ currentpoint ] exch dup 2 get 0 exch R dup 5 get 2 ne {dup dup 6 get exch 4 get {show} {stringwidth pop 0 R} ifelse }if dup 5 get 0 eq {dup 3 get {2 get neg 0 exch R pop} {pop aload pop M} ifelse} {dup 5 get 1 eq {dup 2 get exch dup 3 get exch 6 get stringwidth pop -2 div dup 0 R} {dup 6 get stringwidth pop -2 div 0 R 6 get show 2 index {aload pop M neg 3 -1 roll neg R pop pop} {pop pop pop pop aload pop M} ifelse }ifelse }ifelse } ifelse } forall} bind def /MFwidth {0 exch { dup 5 get 3 ge { 5 get 3 eq { 0 } { pop } ifelse } {dup 3 get{dup dup 0 get findfont exch 1 get scalefont setfont 6 get stringwidth pop add} {pop} ifelse} ifelse} forall} bind def /MLshow { currentpoint stroke M 0 exch R Blacktext {gsave 0 setgray MFshow grestore} {MFshow} ifelse } bind def /MRshow { currentpoint stroke M exch dup MFwidth neg 3 -1 roll R Blacktext {gsave 0 setgray MFshow grestore} {MFshow} ifelse } bind def /MCshow { currentpoint stroke M exch dup MFwidth -2 div 3 -1 roll R Blacktext {gsave 0 setgray MFshow grestore} {MFshow} ifelse } bind def /XYsave { [( ) 1 2 true false 3 ()] } bind def /XYrestore { [( ) 1 2 true false 4 ()] } bind def end %%EndProlog %%Page: 1 1 gnudict begin gsave 50 50 translate 0.100 0.100 scale 90 rotate 0 -5040 translate 0 setgray newpath (Helvetica) findfont 100 scalefont setfont 1.000 UL LTb 510 300 M 63 0 V 6457 0 R -63 0 V stroke 450 300 M [ [(Helvetica) 100.0 0.0 true true 0 ( 0)] ] -33.3 MRshow 1.000 UL LTb 510 934 M 63 0 V 6457 0 R -63 0 V stroke 450 934 M [ [(Helvetica) 100.0 0.0 true true 0 ( 50)] ] -33.3 MRshow 1.000 UL LTb 510 1569 M 63 0 V 6457 0 R -63 0 V stroke 450 1569 M [ [(Helvetica) 100.0 0.0 true true 0 ( 100)] ] -33.3 MRshow 1.000 UL LTb 510 2203 M 63 0 V 6457 0 R -63 0 V stroke 450 2203 M [ [(Helvetica) 100.0 0.0 true true 0 ( 150)] ] -33.3 MRshow 1.000 UL LTb 510 2837 M 63 0 V 6457 0 R -63 0 V stroke 450 2837 M [ [(Helvetica) 100.0 0.0 true true 0 ( 200)] ] -33.3 MRshow 1.000 UL LTb 510 3471 M 63 0 V 6457 0 R -63 0 V stroke 450 3471 M [ [(Helvetica) 100.0 0.0 true true 0 ( 250)] ] -33.3 MRshow 1.000 UL LTb 510 4106 M 63 0 V 6457 0 R -63 0 V stroke 450 4106 M [ [(Helvetica) 100.0 0.0 true true 0 ( 300)] ] -33.3 MRshow 1.000 UL LTb 510 4740 M 63 0 V 6457 0 R -63 0 V stroke 450 4740 M [ [(Helvetica) 100.0 0.0 true true 0 ( 350)] ] -33.3 MRshow 1.000 UL LTb 510 300 M 0 63 V 0 4377 R 0 -63 V stroke 510 200 M [ [(Helvetica) 100.0 0.0 true true 0 ( 0)] ] -33.3 MCshow 1.000 UL LTb 1441 300 M 0 63 V 0 4377 R 0 -63 V stroke 1441 200 M [ [(Helvetica) 100.0 0.0 true true 0 ( 5)] ] -33.3 MCshow 1.000 UL LTb 2373 300 M 0 63 V 0 4377 R 0 -63 V stroke 2373 200 M [ [(Helvetica) 100.0 0.0 true true 0 ( 10)] ] -33.3 MCshow 1.000 UL LTb 3304 300 M 0 63 V 0 4377 R 0 -63 V stroke 3304 200 M [ [(Helvetica) 100.0 0.0 true true 0 ( 15)] ] -33.3 MCshow 1.000 UL LTb 4236 300 M 0 63 V 0 4377 R 0 -63 V stroke 4236 200 M [ [(Helvetica) 100.0 0.0 true true 0 ( 20)] ] -33.3 MCshow 1.000 UL LTb 5167 300 M 0 63 V 0 4377 R 0 -63 V stroke 5167 200 M [ [(Helvetica) 100.0 0.0 true true 0 ( 25)] ] -33.3 MCshow 1.000 UL LTb 6099 300 M 0 63 V 0 4377 R 0 -63 V stroke 6099 200 M [ [(Helvetica) 100.0 0.0 true true 0 ( 30)] ] -33.3 MCshow 1.000 UL LTb 7030 300 M 0 63 V 0 4377 R 0 -63 V stroke 7030 200 M [ [(Helvetica) 100.0 0.0 true true 0 ( 35)] ] -33.3 MCshow 1.000 UL LTb 1.000 UL LTb 510 4740 N 510 300 L 6520 0 V 0 4440 V -6520 0 V Z stroke LCb setrgbcolor 100 2520 M currentpoint gsave translate 90 rotate 0 0 moveto [ [(Helvetica) 100.0 0.0 true true 0 (Processing cost per packet \(10E-6 sec\))] ] -33.3 MCshow grestore LTb LCb setrgbcolor 3770 50 M [ [(Helvetica) 100.0 0.0 true true 0 (Number of nodes)] ] -33.3 MCshow LTb 3770 4890 M [ [(Helvetica) 100.0 0.0 true true 0 (Processing cost for 1000-byte packets)] ] -33.3 MCshow 1.000 UP 1.000 UL LTb 1051 4627 M [ [(Helvetica) 100.0 0.0 true true 0 (Experiment)] ] -33.3 MCshow 1.000 UL LTb 570 4077 N 0 600 V 963 0 V 0 -600 V -963 0 V Z stroke 570 4577 M 963 0 V 1.000 UP stroke LT0 LTb 1110 4527 M [ [(Helvetica) 100.0 0.0 true true 0 (Exp 1)] ] -33.3 MRshow LT0 1170 4527 M 303 0 V 696 442 M 187 43 V 186 92 V 186 82 V 1118 526 V 1863 865 V 696 442 Pls 883 485 Pls 1069 577 Pls 1255 659 Pls 2373 1185 Pls 4236 2050 Pls 1321 4527 Pls 1.000 UP 1.000 UL LT1 LTb 1110 4427 M [ [(Helvetica) 100.0 0.0 true true 0 (Exp 2)] ] -33.3 MRshow LT1 1170 4427 M 303 0 V 696 583 M 187 77 V 186 155 V 186 103 V 1118 660 V 4236 2684 L 696 583 Crs 883 660 Crs 1069 815 Crs 1255 918 Crs 2373 1578 Crs 4236 2684 Crs 1321 4427 Crs 1.000 UP 1.000 UL LT2 LTb 1110 4327 M [ [(Helvetica) 100.0 0.0 true true 0 (Exp 3)] ] -33.3 MRshow LT2 1170 4327 M 303 0 V 696 521 M 883 726 L 186 189 V 186 188 V 2373 2361 L 4236 4119 L 696 521 Star 883 726 Star 1069 915 Star 1255 1103 Star 2373 2361 Star 4236 4119 Star 1321 4327 Star 1.000 UP 1.000 UL LT3 LTb 1110 4227 M [ [(Helvetica) 100.0 0.0 true true 0 (Exp 4)] ] -33.3 MRshow LT3 1170 4227 M 303 0 V 696 456 M 187 23 V 186 30 V 186 28 V 373 53 V 372 53 V 745 129 V 746 108 V 2980 460 V 559 128 V 696 456 Box 883 479 Box 1069 509 Box 1255 537 Box 1628 590 Box 2000 643 Box 2745 772 Box 3491 880 Box 6471 1340 Box 1321 4227 Box 1.000 UP 1.000 UL LT4 LTb 1110 4127 M [ [(Helvetica) 100.0 0.0 true true 0 (Exp 4bis)] ] -33.3 MRshow LT4 1170 4127 M 303 0 V 696 457 M 187 67 V 186 69 V 186 60 V 373 146 V 372 99 V 745 248 V 746 256 V 2980 957 V 559 198 V 696 457 BoxF 883 524 BoxF 1069 593 BoxF 1255 653 BoxF 1628 799 BoxF 2000 898 BoxF 2745 1146 BoxF 3491 1402 BoxF 6471 2359 BoxF 1321 4127 BoxF 1.000 UL LTb 510 4740 N 510 300 L 6520 0 V 0 4440 V -6520 0 V Z stroke 1.000 UP 1.000 UL LTb stroke grestore end showpage %%Trailer %%DocumentFonts: Helvetica %%Pages: 1 nemu-0.3.1/benchmarks/preliminar/comparison-bypkts.gnuplot000066400000000000000000000013261300327754000240370ustar00rootroot00000000000000set terminal postscript colour enhanced landscape lw 1 10 set key box left top width 1 title 'Experiment' set xlabel 'Payload size (UDP packet)' set ylabel 'Processing cost per packet (10E-6 sec)' set title 'Processing cost for a 4-node topology' set xrange [0:1500] plot \ 'results-simu.txt' index 0 every ::24::31 using 1:3 title "Exp 1" with linespoints, \ 'results-simu.txt' index 1 every ::24::31 using 1:3 title "Exp 2" with linespoints, \ 'results-simu.txt' index 3 every ::24::31 using 1:3 title "Exp 3" with linespoints, \ 'results.txt' index 0 every ::39::51 using ($4-42):($10/$3) title "Exp 4" with linespoints, \ 'results.txt' index 1 every ::39::51 using ($4-42):($10/$3) title "Exp 4bis" with linespoints nemu-0.3.1/benchmarks/preliminar/comparison-bypkts.ps000066400000000000000000000512631300327754000227760ustar00rootroot00000000000000%!PS-Adobe-2.0 %%Creator: gnuplot 4.2 patchlevel 3 %%CreationDate: Mon Aug 16 13:02:31 2010 %%DocumentFonts: (atend) %%BoundingBox: 50 50 554 770 %%Orientation: Landscape %%Pages: (atend) %%EndComments %%BeginProlog /gnudict 256 dict def gnudict begin % % The following 6 true/false flags may be edited by hand if required % The unit line width may also be changed % /Color true def /Blacktext false def /Solid false def /Dashlength 1 def /Landscape true def /Level1 false def /Rounded false def /TransparentPatterns false def /gnulinewidth 5.000 def /userlinewidth gnulinewidth def % /vshift -33 def /dl1 { 10.0 Dashlength mul mul Rounded { currentlinewidth 0.75 mul sub dup 0 le { pop 0.01 } if } if } def /dl2 { 10.0 Dashlength mul mul Rounded { currentlinewidth 0.75 mul add } if } def /hpt_ 31.5 def /vpt_ 31.5 def /hpt hpt_ def /vpt vpt_ def Level1 {} { /SDict 10 dict def systemdict /pdfmark known not { userdict /pdfmark systemdict /cleartomark get put } if SDict begin [ /Title () /Subject (gnuplot plot) /Creator (gnuplot 4.2 patchlevel 3 ) /Author (Martin_Hernan Ferrari,L133,2417) % /Producer (gnuplot) % /Keywords () /CreationDate (Mon Aug 16 13:02:31 2010) /DOCINFO pdfmark end } ifelse % % Gnuplot Prolog Version 4.2 (August 2006) % /M {moveto} bind def /L {lineto} bind def /R {rmoveto} bind def /V {rlineto} bind def /N {newpath moveto} bind def /Z {closepath} bind def /C {setrgbcolor} bind def /f {rlineto fill} bind def /vpt2 vpt 2 mul def /hpt2 hpt 2 mul def /Lshow {currentpoint stroke M 0 vshift R Blacktext {gsave 0 setgray show grestore} {show} ifelse} def /Rshow {currentpoint stroke M dup stringwidth pop neg vshift R Blacktext {gsave 0 setgray show grestore} {show} ifelse} def /Cshow {currentpoint stroke M dup stringwidth pop -2 div vshift R Blacktext {gsave 0 setgray show grestore} {show} ifelse} def /UP {dup vpt_ mul /vpt exch def hpt_ mul /hpt exch def /hpt2 hpt 2 mul def /vpt2 vpt 2 mul def} def /DL {Color {setrgbcolor Solid {pop []} if 0 setdash} {pop pop pop 0 setgray Solid {pop []} if 0 setdash} ifelse} def /BL {stroke userlinewidth 2 mul setlinewidth Rounded {1 setlinejoin 1 setlinecap} if} def /AL {stroke userlinewidth 2 div setlinewidth Rounded {1 setlinejoin 1 setlinecap} if} def /UL {dup gnulinewidth mul /userlinewidth exch def dup 1 lt {pop 1} if 10 mul /udl exch def} def /PL {stroke userlinewidth setlinewidth Rounded {1 setlinejoin 1 setlinecap} if} def % Default Line colors /LCw {1 1 1} def /LCb {0 0 0} def /LCa {0 0 0} def /LC0 {1 0 0} def /LC1 {0 1 0} def /LC2 {0 0 1} def /LC3 {1 0 1} def /LC4 {0 1 1} def /LC5 {1 1 0} def /LC6 {0 0 0} def /LC7 {1 0.3 0} def /LC8 {0.5 0.5 0.5} def % Default Line Types /LTw {PL [] 1 setgray} def /LTb {BL [] LCb DL} def /LTa {AL [1 udl mul 2 udl mul] 0 setdash LCa setrgbcolor} def /LT0 {PL [] LC0 DL} def /LT1 {PL [4 dl1 2 dl2] LC1 DL} def /LT2 {PL [2 dl1 3 dl2] LC2 DL} def /LT3 {PL [1 dl1 1.5 dl2] LC3 DL} def /LT4 {PL [6 dl1 2 dl2 1 dl1 2 dl2] LC4 DL} def /LT5 {PL [3 dl1 3 dl2 1 dl1 3 dl2] LC5 DL} def /LT6 {PL [2 dl1 2 dl2 2 dl1 6 dl2] LC6 DL} def /LT7 {PL [1 dl1 2 dl2 6 dl1 2 dl2 1 dl1 2 dl2] LC7 DL} def /LT8 {PL [2 dl1 2 dl2 2 dl1 2 dl2 2 dl1 2 dl2 2 dl1 4 dl2] LC8 DL} def /Pnt {stroke [] 0 setdash gsave 1 setlinecap M 0 0 V stroke grestore} def /Dia {stroke [] 0 setdash 2 copy vpt add M hpt neg vpt neg V hpt vpt neg V hpt vpt V hpt neg vpt V closepath stroke Pnt} def /Pls {stroke [] 0 setdash vpt sub M 0 vpt2 V currentpoint stroke M hpt neg vpt neg R hpt2 0 V stroke } def /Box {stroke [] 0 setdash 2 copy exch hpt sub exch vpt add M 0 vpt2 neg V hpt2 0 V 0 vpt2 V hpt2 neg 0 V closepath stroke Pnt} def /Crs {stroke [] 0 setdash exch hpt sub exch vpt add M hpt2 vpt2 neg V currentpoint stroke M hpt2 neg 0 R hpt2 vpt2 V stroke} def /TriU {stroke [] 0 setdash 2 copy vpt 1.12 mul add M hpt neg vpt -1.62 mul V hpt 2 mul 0 V hpt neg vpt 1.62 mul V closepath stroke Pnt} def /Star {2 copy Pls Crs} def /BoxF {stroke [] 0 setdash exch hpt sub exch vpt add M 0 vpt2 neg V hpt2 0 V 0 vpt2 V hpt2 neg 0 V closepath fill} def /TriUF {stroke [] 0 setdash vpt 1.12 mul add M hpt neg vpt -1.62 mul V hpt 2 mul 0 V hpt neg vpt 1.62 mul V closepath fill} def /TriD {stroke [] 0 setdash 2 copy vpt 1.12 mul sub M hpt neg vpt 1.62 mul V hpt 2 mul 0 V hpt neg vpt -1.62 mul V closepath stroke Pnt} def /TriDF {stroke [] 0 setdash vpt 1.12 mul sub M hpt neg vpt 1.62 mul V hpt 2 mul 0 V hpt neg vpt -1.62 mul V closepath fill} def /DiaF {stroke [] 0 setdash vpt add M hpt neg vpt neg V hpt vpt neg V hpt vpt V hpt neg vpt V closepath fill} def /Pent {stroke [] 0 setdash 2 copy gsave translate 0 hpt M 4 {72 rotate 0 hpt L} repeat closepath stroke grestore Pnt} def /PentF {stroke [] 0 setdash gsave translate 0 hpt M 4 {72 rotate 0 hpt L} repeat closepath fill grestore} def /Circle {stroke [] 0 setdash 2 copy hpt 0 360 arc stroke Pnt} def /CircleF {stroke [] 0 setdash hpt 0 360 arc fill} def /C0 {BL [] 0 setdash 2 copy moveto vpt 90 450 arc} bind def /C1 {BL [] 0 setdash 2 copy moveto 2 copy vpt 0 90 arc closepath fill vpt 0 360 arc closepath} bind def /C2 {BL [] 0 setdash 2 copy moveto 2 copy vpt 90 180 arc closepath fill vpt 0 360 arc closepath} bind def /C3 {BL [] 0 setdash 2 copy moveto 2 copy vpt 0 180 arc closepath fill vpt 0 360 arc closepath} bind def /C4 {BL [] 0 setdash 2 copy moveto 2 copy vpt 180 270 arc closepath fill vpt 0 360 arc closepath} bind def /C5 {BL [] 0 setdash 2 copy moveto 2 copy vpt 0 90 arc 2 copy moveto 2 copy vpt 180 270 arc closepath fill vpt 0 360 arc} bind def /C6 {BL [] 0 setdash 2 copy moveto 2 copy vpt 90 270 arc closepath fill vpt 0 360 arc closepath} bind def /C7 {BL [] 0 setdash 2 copy moveto 2 copy vpt 0 270 arc closepath fill vpt 0 360 arc closepath} bind def /C8 {BL [] 0 setdash 2 copy moveto 2 copy vpt 270 360 arc closepath fill vpt 0 360 arc closepath} bind def /C9 {BL [] 0 setdash 2 copy moveto 2 copy vpt 270 450 arc closepath fill vpt 0 360 arc closepath} bind def /C10 {BL [] 0 setdash 2 copy 2 copy moveto vpt 270 360 arc closepath fill 2 copy moveto 2 copy vpt 90 180 arc closepath fill vpt 0 360 arc closepath} bind def /C11 {BL [] 0 setdash 2 copy moveto 2 copy vpt 0 180 arc closepath fill 2 copy moveto 2 copy vpt 270 360 arc closepath fill vpt 0 360 arc closepath} bind def /C12 {BL [] 0 setdash 2 copy moveto 2 copy vpt 180 360 arc closepath fill vpt 0 360 arc closepath} bind def /C13 {BL [] 0 setdash 2 copy moveto 2 copy vpt 0 90 arc closepath fill 2 copy moveto 2 copy vpt 180 360 arc closepath fill vpt 0 360 arc closepath} bind def /C14 {BL [] 0 setdash 2 copy moveto 2 copy vpt 90 360 arc closepath fill vpt 0 360 arc} bind def /C15 {BL [] 0 setdash 2 copy vpt 0 360 arc closepath fill vpt 0 360 arc closepath} bind def /Rec {newpath 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto neg 0 rlineto closepath} bind def /Square {dup Rec} bind def /Bsquare {vpt sub exch vpt sub exch vpt2 Square} bind def /S0 {BL [] 0 setdash 2 copy moveto 0 vpt rlineto BL Bsquare} bind def /S1 {BL [] 0 setdash 2 copy vpt Square fill Bsquare} bind def /S2 {BL [] 0 setdash 2 copy exch vpt sub exch vpt Square fill Bsquare} bind def /S3 {BL [] 0 setdash 2 copy exch vpt sub exch vpt2 vpt Rec fill Bsquare} bind def /S4 {BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt Square fill Bsquare} bind def /S5 {BL [] 0 setdash 2 copy 2 copy vpt Square fill exch vpt sub exch vpt sub vpt Square fill Bsquare} bind def /S6 {BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt vpt2 Rec fill Bsquare} bind def /S7 {BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt vpt2 Rec fill 2 copy vpt Square fill Bsquare} bind def /S8 {BL [] 0 setdash 2 copy vpt sub vpt Square fill Bsquare} bind def /S9 {BL [] 0 setdash 2 copy vpt sub vpt vpt2 Rec fill Bsquare} bind def /S10 {BL [] 0 setdash 2 copy vpt sub vpt Square fill 2 copy exch vpt sub exch vpt Square fill Bsquare} bind def /S11 {BL [] 0 setdash 2 copy vpt sub vpt Square fill 2 copy exch vpt sub exch vpt2 vpt Rec fill Bsquare} bind def /S12 {BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt2 vpt Rec fill Bsquare} bind def /S13 {BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt2 vpt Rec fill 2 copy vpt Square fill Bsquare} bind def /S14 {BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt2 vpt Rec fill 2 copy exch vpt sub exch vpt Square fill Bsquare} bind def /S15 {BL [] 0 setdash 2 copy Bsquare fill Bsquare} bind def /D0 {gsave translate 45 rotate 0 0 S0 stroke grestore} bind def /D1 {gsave translate 45 rotate 0 0 S1 stroke grestore} bind def /D2 {gsave translate 45 rotate 0 0 S2 stroke grestore} bind def /D3 {gsave translate 45 rotate 0 0 S3 stroke grestore} bind def /D4 {gsave translate 45 rotate 0 0 S4 stroke grestore} bind def /D5 {gsave translate 45 rotate 0 0 S5 stroke grestore} bind def /D6 {gsave translate 45 rotate 0 0 S6 stroke grestore} bind def /D7 {gsave translate 45 rotate 0 0 S7 stroke grestore} bind def /D8 {gsave translate 45 rotate 0 0 S8 stroke grestore} bind def /D9 {gsave translate 45 rotate 0 0 S9 stroke grestore} bind def /D10 {gsave translate 45 rotate 0 0 S10 stroke grestore} bind def /D11 {gsave translate 45 rotate 0 0 S11 stroke grestore} bind def /D12 {gsave translate 45 rotate 0 0 S12 stroke grestore} bind def /D13 {gsave translate 45 rotate 0 0 S13 stroke grestore} bind def /D14 {gsave translate 45 rotate 0 0 S14 stroke grestore} bind def /D15 {gsave translate 45 rotate 0 0 S15 stroke grestore} bind def /DiaE {stroke [] 0 setdash vpt add M hpt neg vpt neg V hpt vpt neg V hpt vpt V hpt neg vpt V closepath stroke} def /BoxE {stroke [] 0 setdash exch hpt sub exch vpt add M 0 vpt2 neg V hpt2 0 V 0 vpt2 V hpt2 neg 0 V closepath stroke} def /TriUE {stroke [] 0 setdash vpt 1.12 mul add M hpt neg vpt -1.62 mul V hpt 2 mul 0 V hpt neg vpt 1.62 mul V closepath stroke} def /TriDE {stroke [] 0 setdash vpt 1.12 mul sub M hpt neg vpt 1.62 mul V hpt 2 mul 0 V hpt neg vpt -1.62 mul V closepath stroke} def /PentE {stroke [] 0 setdash gsave translate 0 hpt M 4 {72 rotate 0 hpt L} repeat closepath stroke grestore} def /CircE {stroke [] 0 setdash hpt 0 360 arc stroke} def /Opaque {gsave closepath 1 setgray fill grestore 0 setgray closepath} def /DiaW {stroke [] 0 setdash vpt add M hpt neg vpt neg V hpt vpt neg V hpt vpt V hpt neg vpt V Opaque stroke} def /BoxW {stroke [] 0 setdash exch hpt sub exch vpt add M 0 vpt2 neg V hpt2 0 V 0 vpt2 V hpt2 neg 0 V Opaque stroke} def /TriUW {stroke [] 0 setdash vpt 1.12 mul add M hpt neg vpt -1.62 mul V hpt 2 mul 0 V hpt neg vpt 1.62 mul V Opaque stroke} def /TriDW {stroke [] 0 setdash vpt 1.12 mul sub M hpt neg vpt 1.62 mul V hpt 2 mul 0 V hpt neg vpt -1.62 mul V Opaque stroke} def /PentW {stroke [] 0 setdash gsave translate 0 hpt M 4 {72 rotate 0 hpt L} repeat Opaque stroke grestore} def /CircW {stroke [] 0 setdash hpt 0 360 arc Opaque stroke} def /BoxFill {gsave Rec 1 setgray fill grestore} def /Density { /Fillden exch def currentrgbcolor /ColB exch def /ColG exch def /ColR exch def /ColR ColR Fillden mul Fillden sub 1 add def /ColG ColG Fillden mul Fillden sub 1 add def /ColB ColB Fillden mul Fillden sub 1 add def ColR ColG ColB setrgbcolor} def /BoxColFill {gsave Rec PolyFill} def /PolyFill {gsave Density fill grestore grestore} def /h {rlineto rlineto rlineto gsave closepath fill grestore} bind def % % PostScript Level 1 Pattern Fill routine for rectangles % Usage: x y w h s a XX PatternFill % x,y = lower left corner of box to be filled % w,h = width and height of box % a = angle in degrees between lines and x-axis % XX = 0/1 for no/yes cross-hatch % /PatternFill {gsave /PFa [ 9 2 roll ] def PFa 0 get PFa 2 get 2 div add PFa 1 get PFa 3 get 2 div add translate PFa 2 get -2 div PFa 3 get -2 div PFa 2 get PFa 3 get Rec gsave 1 setgray fill grestore clip currentlinewidth 0.5 mul setlinewidth /PFs PFa 2 get dup mul PFa 3 get dup mul add sqrt def 0 0 M PFa 5 get rotate PFs -2 div dup translate 0 1 PFs PFa 4 get div 1 add floor cvi {PFa 4 get mul 0 M 0 PFs V} for 0 PFa 6 get ne { 0 1 PFs PFa 4 get div 1 add floor cvi {PFa 4 get mul 0 2 1 roll M PFs 0 V} for } if stroke grestore} def % /languagelevel where {pop languagelevel} {1} ifelse 2 lt {/InterpretLevel1 true def} {/InterpretLevel1 Level1 def} ifelse % % PostScript level 2 pattern fill definitions % /Level2PatternFill { /Tile8x8 {/PaintType 2 /PatternType 1 /TilingType 1 /BBox [0 0 8 8] /XStep 8 /YStep 8} bind def /KeepColor {currentrgbcolor [/Pattern /DeviceRGB] setcolorspace} bind def << Tile8x8 /PaintProc {0.5 setlinewidth pop 0 0 M 8 8 L 0 8 M 8 0 L stroke} >> matrix makepattern /Pat1 exch def << Tile8x8 /PaintProc {0.5 setlinewidth pop 0 0 M 8 8 L 0 8 M 8 0 L stroke 0 4 M 4 8 L 8 4 L 4 0 L 0 4 L stroke} >> matrix makepattern /Pat2 exch def << Tile8x8 /PaintProc {0.5 setlinewidth pop 0 0 M 0 8 L 8 8 L 8 0 L 0 0 L fill} >> matrix makepattern /Pat3 exch def << Tile8x8 /PaintProc {0.5 setlinewidth pop -4 8 M 8 -4 L 0 12 M 12 0 L stroke} >> matrix makepattern /Pat4 exch def << Tile8x8 /PaintProc {0.5 setlinewidth pop -4 0 M 8 12 L 0 -4 M 12 8 L stroke} >> matrix makepattern /Pat5 exch def << Tile8x8 /PaintProc {0.5 setlinewidth pop -2 8 M 4 -4 L 0 12 M 8 -4 L 4 12 M 10 0 L stroke} >> matrix makepattern /Pat6 exch def << Tile8x8 /PaintProc {0.5 setlinewidth pop -2 0 M 4 12 L 0 -4 M 8 12 L 4 -4 M 10 8 L stroke} >> matrix makepattern /Pat7 exch def << Tile8x8 /PaintProc {0.5 setlinewidth pop 8 -2 M -4 4 L 12 0 M -4 8 L 12 4 M 0 10 L stroke} >> matrix makepattern /Pat8 exch def << Tile8x8 /PaintProc {0.5 setlinewidth pop 0 -2 M 12 4 L -4 0 M 12 8 L -4 4 M 8 10 L stroke} >> matrix makepattern /Pat9 exch def /Pattern1 {PatternBgnd KeepColor Pat1 setpattern} bind def /Pattern2 {PatternBgnd KeepColor Pat2 setpattern} bind def /Pattern3 {PatternBgnd KeepColor Pat3 setpattern} bind def /Pattern4 {PatternBgnd KeepColor Landscape {Pat5} {Pat4} ifelse setpattern} bind def /Pattern5 {PatternBgnd KeepColor Landscape {Pat4} {Pat5} ifelse setpattern} bind def /Pattern6 {PatternBgnd KeepColor Landscape {Pat9} {Pat6} ifelse setpattern} bind def /Pattern7 {PatternBgnd KeepColor Landscape {Pat8} {Pat7} ifelse setpattern} bind def } def % % %End of PostScript Level 2 code % /PatternBgnd { TransparentPatterns {} {gsave 1 setgray fill grestore} ifelse } def % % Substitute for Level 2 pattern fill codes with % grayscale if Level 2 support is not selected. % /Level1PatternFill { /Pattern1 {0.250 Density} bind def /Pattern2 {0.500 Density} bind def /Pattern3 {0.750 Density} bind def /Pattern4 {0.125 Density} bind def /Pattern5 {0.375 Density} bind def /Pattern6 {0.625 Density} bind def /Pattern7 {0.875 Density} bind def } def % % Now test for support of Level 2 code % Level1 {Level1PatternFill} {Level2PatternFill} ifelse % /Symbol-Oblique /Symbol findfont [1 0 .167 1 0 0] makefont dup length dict begin {1 index /FID eq {pop pop} {def} ifelse} forall currentdict end definefont pop /MFshow { { dup 5 get 3 ge { 5 get 3 eq {gsave} {grestore} ifelse } {dup dup 0 get findfont exch 1 get scalefont setfont [ currentpoint ] exch dup 2 get 0 exch R dup 5 get 2 ne {dup dup 6 get exch 4 get {show} {stringwidth pop 0 R} ifelse }if dup 5 get 0 eq {dup 3 get {2 get neg 0 exch R pop} {pop aload pop M} ifelse} {dup 5 get 1 eq {dup 2 get exch dup 3 get exch 6 get stringwidth pop -2 div dup 0 R} {dup 6 get stringwidth pop -2 div 0 R 6 get show 2 index {aload pop M neg 3 -1 roll neg R pop pop} {pop pop pop pop aload pop M} ifelse }ifelse }ifelse } ifelse } forall} bind def /MFwidth {0 exch { dup 5 get 3 ge { 5 get 3 eq { 0 } { pop } ifelse } {dup 3 get{dup dup 0 get findfont exch 1 get scalefont setfont 6 get stringwidth pop add} {pop} ifelse} ifelse} forall} bind def /MLshow { currentpoint stroke M 0 exch R Blacktext {gsave 0 setgray MFshow grestore} {MFshow} ifelse } bind def /MRshow { currentpoint stroke M exch dup MFwidth neg 3 -1 roll R Blacktext {gsave 0 setgray MFshow grestore} {MFshow} ifelse } bind def /MCshow { currentpoint stroke M exch dup MFwidth -2 div 3 -1 roll R Blacktext {gsave 0 setgray MFshow grestore} {MFshow} ifelse } bind def /XYsave { [( ) 1 2 true false 3 ()] } bind def /XYrestore { [( ) 1 2 true false 4 ()] } bind def end %%EndProlog %%Page: 1 1 gnudict begin gsave 50 50 translate 0.100 0.100 scale 90 rotate 0 -5040 translate 0 setgray newpath (Helvetica) findfont 100 scalefont setfont 1.000 UL LTb 450 300 M 63 0 V 6517 0 R -63 0 V stroke 390 300 M [ [(Helvetica) 100.0 0.0 true true 0 ( 10)] ] -33.3 MRshow 1.000 UL LTb 450 1040 M 63 0 V 6517 0 R -63 0 V stroke 390 1040 M [ [(Helvetica) 100.0 0.0 true true 0 ( 20)] ] -33.3 MRshow 1.000 UL LTb 450 1780 M 63 0 V 6517 0 R -63 0 V stroke 390 1780 M [ [(Helvetica) 100.0 0.0 true true 0 ( 30)] ] -33.3 MRshow 1.000 UL LTb 450 2520 M 63 0 V 6517 0 R -63 0 V stroke 390 2520 M [ [(Helvetica) 100.0 0.0 true true 0 ( 40)] ] -33.3 MRshow 1.000 UL LTb 450 3260 M 63 0 V 6517 0 R -63 0 V stroke 390 3260 M [ [(Helvetica) 100.0 0.0 true true 0 ( 50)] ] -33.3 MRshow 1.000 UL LTb 450 4000 M 63 0 V 6517 0 R -63 0 V stroke 390 4000 M [ [(Helvetica) 100.0 0.0 true true 0 ( 60)] ] -33.3 MRshow 1.000 UL LTb 450 4740 M 63 0 V 6517 0 R -63 0 V stroke 390 4740 M [ [(Helvetica) 100.0 0.0 true true 0 ( 70)] ] -33.3 MRshow 1.000 UL LTb 450 300 M 0 63 V 0 4377 R 0 -63 V stroke 450 200 M [ [(Helvetica) 100.0 0.0 true true 0 ( 0)] ] -33.3 MCshow 1.000 UL LTb 1327 300 M 0 63 V 0 4377 R 0 -63 V stroke 1327 200 M [ [(Helvetica) 100.0 0.0 true true 0 ( 200)] ] -33.3 MCshow 1.000 UL LTb 2205 300 M 0 63 V 0 4377 R 0 -63 V stroke 2205 200 M [ [(Helvetica) 100.0 0.0 true true 0 ( 400)] ] -33.3 MCshow 1.000 UL LTb 3082 300 M 0 63 V 0 4377 R 0 -63 V stroke 3082 200 M [ [(Helvetica) 100.0 0.0 true true 0 ( 600)] ] -33.3 MCshow 1.000 UL LTb 3959 300 M 0 63 V 0 4377 R 0 -63 V stroke 3959 200 M [ [(Helvetica) 100.0 0.0 true true 0 ( 800)] ] -33.3 MCshow 1.000 UL LTb 4837 300 M 0 63 V 0 4377 R 0 -63 V stroke 4837 200 M [ [(Helvetica) 100.0 0.0 true true 0 ( 1000)] ] -33.3 MCshow 1.000 UL LTb 5714 300 M 0 63 V 0 4377 R 0 -63 V stroke 5714 200 M [ [(Helvetica) 100.0 0.0 true true 0 ( 1200)] ] -33.3 MCshow 1.000 UL LTb 6591 300 M 0 63 V 0 4377 R 0 -63 V stroke 6591 200 M [ [(Helvetica) 100.0 0.0 true true 0 ( 1400)] ] -33.3 MCshow 1.000 UL LTb 1.000 UL LTb 450 4740 N 450 300 L 6580 0 V 0 4440 V -6580 0 V Z stroke LCb setrgbcolor 100 2520 M currentpoint gsave translate 90 rotate 0 0 moveto [ [(Helvetica) 100.0 0.0 true true 0 (Processing cost per packet \(10E-6 sec\))] ] -33.3 MCshow grestore LTb LCb setrgbcolor 3740 50 M [ [(Helvetica) 100.0 0.0 true true 0 (Payload size \(UDP packet\))] ] -33.3 MCshow LTb 3740 4890 M [ [(Helvetica) 100.0 0.0 true true 0 (Processing cost for a 4-node topology)] ] -33.3 MCshow 1.000 UP 1.000 UL LTb 991 4627 M [ [(Helvetica) 100.0 0.0 true true 0 (Experiment)] ] -33.3 MCshow 1.000 UL LTb 510 4077 N 0 600 V 963 0 V 0 -600 V -963 0 V Z stroke 510 4577 M 963 0 V 1.000 UP stroke LT0 LTb 1050 4527 M [ [(Helvetica) 100.0 0.0 true true 0 (Exp 1)] ] -33.3 MRshow LT0 1110 4527 M 303 0 V 6591 1727 M 4837 1653 L 3082 1640 L -877 -31 V 889 1587 L -88 -21 V 582 1780 L -88 788 V 6591 1727 Pls 4837 1653 Pls 3082 1640 Pls 2205 1609 Pls 889 1587 Pls 801 1566 Pls 582 1780 Pls 494 2568 Pls 1261 4527 Pls 1.000 UP 1.000 UL LT1 LTb 1050 4427 M [ [(Helvetica) 100.0 0.0 true true 0 (Exp 2)] ] -33.3 MRshow LT1 1110 4427 M 303 0 V 6591 3321 M 4837 3166 L -1755 70 V 2205 2635 L 889 2623 L 801 2523 L 582 2485 L -88 778 V 6591 3321 Crs 4837 3166 Crs 3082 3236 Crs 2205 2635 Crs 889 2623 Crs 801 2523 Crs 582 2485 Crs 494 3263 Crs 1261 4427 Crs 1.000 UP 1.000 UL LT2 LTb 1050 4327 M [ [(Helvetica) 100.0 0.0 true true 0 (Exp 3)] ] -33.3 MRshow LT2 1110 4327 M 303 0 V 5178 364 R 4837 4466 L 3082 4124 L 2205 3891 L 889 3743 L 801 3618 L 582 3562 L -88 787 V 6591 4691 Star 4837 4466 Star 3082 4124 Star 2205 3891 Star 889 3743 Star 801 3618 Star 582 3562 Star 494 4349 Star 1261 4327 Star 1.000 UP 1.000 UL LT3 LTb 1050 4227 M [ [(Helvetica) 100.0 0.0 true true 0 (Exp 4)] ] -33.3 MRshow LT3 1110 4227 M 303 0 V 450 871 M 4 44 V 5 -25 V 9 6 V 17 67 V 35 -75 V 70 13 V 731 886 L 280 12 V 562 5 V 1123 22 V 2246 17 V 1904 48 V 450 871 Box 454 915 Box 459 890 Box 468 896 Box 485 963 Box 520 888 Box 590 901 Box 731 886 Box 1011 898 Box 1573 903 Box 2696 925 Box 4942 942 Box 6846 990 Box 1261 4227 Box 1.000 UP 1.000 UL LT4 LTb 1050 4127 M [ [(Helvetica) 100.0 0.0 true true 0 (Exp 4bis)] ] -33.3 MRshow LT4 1110 4127 M 303 0 V 450 1518 M 4 20 V 5 9 V 9 3 V 17 -10 V 35 5 V 70 -4 V 141 8 V 280 2 V 562 15 V 1123 1 V 2246 50 V 1904 12 V 450 1518 BoxF 454 1538 BoxF 459 1547 BoxF 468 1550 BoxF 485 1540 BoxF 520 1545 BoxF 590 1541 BoxF 731 1549 BoxF 1011 1551 BoxF 1573 1566 BoxF 2696 1567 BoxF 4942 1617 BoxF 6846 1629 BoxF 1261 4127 BoxF 1.000 UL LTb 450 4740 N 450 300 L 6580 0 V 0 4440 V -6580 0 V Z stroke 1.000 UP 1.000 UL LTb stroke grestore end showpage %%Trailer %%DocumentFonts: Helvetica %%Pages: 1 nemu-0.3.1/benchmarks/preliminar/linear-raw-throughput.txt000066400000000000000000000542001300327754000237510ustar00rootroot00000000000000Nodes,Bytes received,Packets received,Packet size,Payload size,Errors,Min delay,Average delay,Max delay,Jitter,Total time,Link-level bandwidth,Command line 1,35958426,856153,42,0,0,-1,0,0,,10000007,28766720,--format=csv -n 1 -s 42 --use-p2p 1,36061242,858601,42,0,0,-1,0,0,,10000004,28848982,--format=csv -n 1 -s 42 1,36350867,845369,43,1,0,-1,0,0,,10000009,29080667,--format=csv -n 1 -s 43 --use-p2p 1,36353920,845440,43,1,0,-1,0,0,,10000007,29083115,--format=csv -n 1 -s 43 1,37097060,843115,44,2,0,-1,0,0,,10000005,29677633,--format=csv -n 1 -s 44 --use-p2p 1,36964840,840110,44,2,0,-1,0,0,,10000003,29571863,--format=csv -n 1 -s 44 1,38149180,829330,46,4,0,-1,0,0,,10000006,30519325,--format=csv -n 1 -s 46 --use-p2p 1,38546988,837978,46,4,0,-1,0,0,,10000004,30837578,--format=csv -n 1 -s 46 1,41846900,836938,50,8,0,8,8,672,,10000006,33477499,--format=csv -n 1 -s 50 --use-p2p 1,41930800,838616,50,8,0,8,8,646,,10000010,33544606,--format=csv -n 1 -s 50 1,48969342,844299,58,16,0,8,8,237,,10000002,39175465,--format=csv -n 1 -s 58 --use-p2p 1,48604464,838008,58,16,0,8,8,669,,10000001,38883567,--format=csv -n 1 -s 58 1,61989578,837697,74,32,0,8,8,644,,10000009,49591617,--format=csv -n 1 -s 74 --use-p2p 1,62043820,838430,74,32,0,8,8,197,,10000006,49635026,--format=csv -n 1 -s 74 1,89067030,840255,106,64,0,8,8,198,,10000009,71253559,--format=csv -n 1 -s 106 --use-p2p 1,88529822,835187,106,64,0,8,8,643,,10000008,70823800,--format=csv -n 1 -s 106 1,141704690,833557,170,128,0,8,8,641,,10000002,113363729,--format=csv -n 1 -s 170 --use-p2p 1,141857690,834457,170,128,0,8,8,204,,10000005,113486095,--format=csv -n 1 -s 170 1,249269550,836475,298,256,0,8,8,197,,10000011,199415420,--format=csv -n 1 -s 298 --use-p2p 1,248590706,834197,298,256,0,8,8,199,,10000005,198872465,--format=csv -n 1 -s 298 1,454933720,821180,554,512,0,8,9,199,,10000010,363946612,--format=csv -n 1 -s 554 --use-p2p 1,457866596,826474,554,512,0,8,8,198,,10000006,366293057,--format=csv -n 1 -s 554 1,865598396,812006,1066,1024,0,8,9,269,,10000005,692478370,--format=csv -n 1 -s 1066 --use-p2p 1,859640522,806417,1066,1024,0,8,9,658,,10000004,687712142,--format=csv -n 1 -s 1066 1,1189230000,792820,1500,1458,0,9,9,199,,10000004,951383619,--format=csv -n 1 -s 1500 --use-p2p 1,1191405000,794270,1500,1458,0,9,9,519,,10000010,953123046,--format=csv -n 1 -s 1500 2,32702628,778634,42,0,0,-1,0,0,,10000006,26162086,--format=csv -n 2 -s 42 --use-p2p 2,25154388,598914,42,0,0,-1,0,0,,10000004,20123502,--format=csv -n 2 -s 42 2,32755207,761749,43,1,0,-1,0,0,,10000003,26204157,--format=csv -n 2 -s 43 --use-p2p 2,25566725,594575,43,1,0,-1,0,0,,10000004,20453371,--format=csv -n 2 -s 43 2,33459712,760448,44,2,0,-1,0,0,,10000006,26767753,--format=csv -n 2 -s 44 --use-p2p 2,26056624,592196,44,2,0,-1,0,0,,10000002,20845295,--format=csv -n 2 -s 44 2,34714682,754667,46,4,0,-1,0,0,,10000007,27771726,--format=csv -n 2 -s 46 --use-p2p 2,27253114,592459,46,4,0,-1,0,0,,10000011,21802467,--format=csv -n 2 -s 46 2,37886350,757727,50,8,0,9,9,680,,10000002,30309073,--format=csv -n 2 -s 50 --use-p2p 2,29665550,593311,50,8,0,13,13,325,,10000011,23732413,--format=csv -n 2 -s 50 2,44028786,759117,58,16,0,9,10,207,,10000006,35223007,--format=csv -n 2 -s 58 --use-p2p 2,34419984,593448,58,16,0,13,13,619,,10000015,27535945,--format=csv -n 2 -s 58 2,56526676,763874,74,32,0,9,9,211,,10000000,45221340,--format=csv -n 2 -s 74 --use-p2p 2,44018826,594849,74,32,0,13,13,290,,10000011,35215022,--format=csv -n 2 -s 74 2,80488026,759321,106,64,0,9,9,835,,10000003,64390401,--format=csv -n 2 -s 106 --use-p2p 2,62710554,591609,106,64,0,13,13,608,,10000007,50168408,--format=csv -n 2 -s 106 2,128596670,756451,170,128,0,9,10,2479,,10000006,102877274,--format=csv -n 2 -s 170 --use-p2p 2,100280960,589888,170,128,0,13,13,609,,10000000,80224768,--format=csv -n 2 -s 170 2,222942144,748128,298,256,0,9,10,665,,10000008,178353572,--format=csv -n 2 -s 298 --use-p2p 2,174893518,586891,298,256,0,13,13,205,,10000005,139914744,--format=csv -n 2 -s 298 2,413247436,745934,554,512,0,9,10,3599,,10000011,330597585,--format=csv -n 2 -s 554 --use-p2p 2,322896130,582845,554,512,0,13,13,3022,,10000008,258316697,--format=csv -n 2 -s 554 2,754548912,707832,1066,1024,0,10,10,2914,,10000010,603638525,--format=csv -n 2 -s 1066 --use-p2p 2,603938036,566546,1066,1024,0,13,14,1565,,10000013,483149800,--format=csv -n 2 -s 1066 2,1072513500,715009,1500,1458,0,10,10,2476,,10000011,858009856,--format=csv -n 2 -s 1500 --use-p2p 2,837000000,558000,1500,1458,0,14,14,610,,10000008,669599464,--format=csv -n 2 -s 1500 3,26998440,642820,42,0,0,-1,0,0,,10000009,21598732,--format=csv -n 3 -s 42 --use-p2p 3,19445160,462980,42,0,0,-1,0,0,,10000001,15556126,--format=csv -n 3 -s 42 3,27304828,634996,43,1,0,-1,0,0,,10000004,21843853,--format=csv -n 3 -s 43 --use-p2p 3,19648205,456935,43,1,0,-1,0,0,,10000008,15718551,--format=csv -n 3 -s 43 3,27947876,635179,44,2,0,-1,0,0,,10000011,22358276,--format=csv -n 3 -s 44 --use-p2p 3,19805412,450123,44,2,0,-1,0,0,,10000001,15844328,--format=csv -n 3 -s 44 3,29036120,631220,46,4,0,-1,0,0,,10000011,23228870,--format=csv -n 3 -s 46 --use-p2p 3,20852628,453318,46,4,0,-1,0,0,,10000001,16682100,--format=csv -n 3 -s 46 3,31800550,636011,50,8,0,12,12,318,,10000000,25440440,--format=csv -n 3 -s 50 --use-p2p 3,22827550,456551,50,8,0,18,18,409,,10000015,18262012,--format=csv -n 3 -s 50 3,36802392,634524,58,16,0,12,12,6536,,10000007,29441892,--format=csv -n 3 -s 58 --use-p2p 3,26451190,456055,58,16,0,18,18,6325,,10000013,21160924,--format=csv -n 3 -s 58 3,46947820,634430,74,32,0,12,12,480,,10000007,37558229,--format=csv -n 3 -s 74 --use-p2p 3,33574318,453707,74,32,0,18,18,3024,,10000006,26859438,--format=csv -n 3 -s 74 3,67244598,634383,106,64,0,12,12,3022,,10000012,53795613,--format=csv -n 3 -s 106 --use-p2p 3,48324022,455887,106,64,0,18,18,569,,10000006,38659194,--format=csv -n 3 -s 106 3,107662700,633310,170,128,0,12,12,262,,10000014,86130039,--format=csv -n 3 -s 170 --use-p2p 3,77066950,453335,170,128,0,18,18,619,,10000014,61653473,--format=csv -n 3 -s 170 3,175495478,588911,298,256,0,12,12,2506,,10000007,140396284,--format=csv -n 3 -s 298 --use-p2p 3,134931122,452789,298,256,0,18,18,605,,10000011,107944778,--format=csv -n 3 -s 298 3,343163666,619429,554,512,0,12,12,303,,10000006,274530768,--format=csv -n 3 -s 554 --use-p2p 3,249327700,450050,554,512,0,18,18,3026,,10000013,199461900,--format=csv -n 3 -s 554 3,645981076,605986,1066,1024,0,12,13,302,,10000001,516784809,--format=csv -n 3 -s 1066 --use-p2p 3,460952258,432413,1066,1024,0,18,19,222,,10000011,368761400,--format=csv -n 3 -s 1066 3,892099500,594733,1500,1458,0,13,13,596,,10000005,713679243,--format=csv -n 3 -s 1500 --use-p2p 3,653416500,435611,1500,1458,0,18,19,217,,10000002,522733095,--format=csv -n 3 -s 1500 4,23705682,564421,42,0,0,-1,0,0,,10000013,18964520,--format=csv -n 4 -s 42 --use-p2p 4,15876294,378007,42,0,0,-1,0,0,,10000012,12701019,--format=csv -n 4 -s 42 4,23482214,546098,43,1,0,-1,0,0,,10000011,18785750,--format=csv -n 4 -s 43 --use-p2p 4,16089009,374163,43,1,0,-1,0,0,,10000012,12871191,--format=csv -n 4 -s 43 4,24475396,556259,44,2,0,-1,0,0,,10000010,19580297,--format=csv -n 4 -s 44 --use-p2p 4,16384016,372364,44,2,0,-1,0,0,,10000006,13107204,--format=csv -n 4 -s 44 4,25477376,553856,46,4,0,-1,0,0,,10000009,20381882,--format=csv -n 4 -s 46 --use-p2p 4,17101604,371774,46,4,0,-1,0,0,,10000022,13681253,--format=csv -n 4 -s 46 4,26366100,527322,50,8,0,14,14,2328,,10000010,21092858,--format=csv -n 4 -s 50 --use-p2p 4,18684800,373696,50,8,0,22,23,266,,10000005,14947832,--format=csv -n 4 -s 50 4,32330244,557418,58,16,0,14,14,3020,,10000003,25864187,--format=csv -n 4 -s 58 --use-p2p 4,21619152,372744,58,16,0,22,23,621,,10000020,17295287,--format=csv -n 4 -s 58 4,40828464,551736,74,32,0,14,14,1992,,10000014,32662725,--format=csv -n 4 -s 74 --use-p2p 4,27643662,373563,74,32,0,22,23,619,,10000017,22114892,--format=csv -n 4 -s 74 4,59176514,558269,106,64,0,14,14,3022,,10000003,47341196,--format=csv -n 4 -s 106 --use-p2p 4,39427442,371957,106,64,0,22,23,2133,,10000019,31541893,--format=csv -n 4 -s 106 4,94033630,553139,170,128,0,14,14,2428,,10000010,75226828,--format=csv -n 4 -s 170 --use-p2p 4,63178290,371637,170,128,0,22,23,686,,10000018,50542541,--format=csv -n 4 -s 170 4,164163134,550883,298,256,0,14,14,1962,,10000015,131330310,--format=csv -n 4 -s 298 --use-p2p 4,109951570,368965,298,256,0,22,23,3002,,10000002,87961238,--format=csv -n 4 -s 298 4,300400960,542240,554,512,0,14,14,2925,,10000017,240320359,--format=csv -n 4 -s 554 --use-p2p 4,204265340,368710,554,512,0,23,23,3023,,10000002,163412239,--format=csv -n 4 -s 554 4,570597820,535270,1066,1024,0,14,15,6391,,10000011,456477753,--format=csv -n 4 -s 1066 --use-p2p 4,383543602,359797,1066,1024,0,23,24,2667,,10000023,306834175,--format=csv -n 4 -s 1066 4,775998000,517332,1500,1458,0,15,15,3719,,10000009,620797841,--format=csv -n 4 -s 1500 --use-p2p 4,536488500,357659,1500,1458,0,23,24,2659,,10000017,429190070,--format=csv -n 4 -s 1500 6,19315632,459896,42,0,0,-1,0,0,,10000000,15452505,--format=csv -n 6 -s 42 --use-p2p 6,11193042,266501,42,0,0,-1,0,0,,10000006,8954428,--format=csv -n 6 -s 42 6,18623257,433099,43,1,0,-1,0,0,,10000014,14898584,--format=csv -n 6 -s 43 --use-p2p 6,11817819,274833,43,1,0,-1,0,0,,10000007,9454248,--format=csv -n 6 -s 43 6,19882412,451873,44,2,0,-1,0,0,,10000010,15905913,--format=csv -n 6 -s 44 --use-p2p 6,12089792,274768,44,2,0,-1,0,0,,10000031,9671803,--format=csv -n 6 -s 44 6,20776130,451655,46,4,0,-1,0,0,,10000013,16620882,--format=csv -n 6 -s 46 --use-p2p 6,12628334,274529,46,4,0,-1,0,0,,10000026,10102640,--format=csv -n 6 -s 46 6,22367350,447347,50,8,0,18,19,2467,,10000013,17893856,--format=csv -n 6 -s 50 --use-p2p 6,13735350,274707,50,8,0,32,32,673,,10000034,10988242,--format=csv -n 6 -s 50 6,26372600,454700,58,16,0,18,18,597,,10000001,21098077,--format=csv -n 6 -s 58 --use-p2p 6,15723220,271090,58,16,0,32,32,623,,10000021,12578549,--format=csv -n 6 -s 58 6,33213050,448825,74,32,0,18,18,2423,,10000001,26570437,--format=csv -n 6 -s 74 --use-p2p 6,20231304,273396,74,32,0,32,33,628,,10000022,16185007,--format=csv -n 6 -s 74 6,47678906,449801,106,64,0,18,18,588,,10000010,38143086,--format=csv -n 6 -s 106 --use-p2p 6,29065094,274199,106,64,0,32,32,624,,10000029,23252007,--format=csv -n 6 -s 106 6,76695160,451148,170,128,0,18,18,346,,10000009,61356072,--format=csv -n 6 -s 170 --use-p2p 6,46496870,273511,170,128,0,32,32,2425,,10000003,37197484,--format=csv -n 6 -s 170 6,134036526,449787,298,256,0,18,18,810,,10000020,107229006,--format=csv -n 6 -s 298 --use-p2p 6,81152254,272323,298,256,0,32,33,623,,10000000,64921803,--format=csv -n 6 -s 298 6,247951564,447566,554,512,0,18,19,2903,,10000012,198361013,--format=csv -n 6 -s 554 --use-p2p 6,145439958,262527,554,512,0,32,33,956,,10000006,116351896,--format=csv -n 6 -s 554 6,466302512,437432,1066,1024,0,18,19,3024,,10000001,373041972,--format=csv -n 6 -s 1066 --use-p2p 6,271252228,254458,1066,1024,0,32,33,2727,,10000004,217001695,--format=csv -n 6 -s 1066 6,644083500,429389,1500,1458,0,19,19,2389,,10000005,515266542,--format=csv -n 6 -s 1500 --use-p2p 6,400338000,266892,1500,1458,0,33,33,625,,10000024,320269631,--format=csv -n 6 -s 1500 8,16154670,384635,42,0,0,-1,0,0,,10000001,12923734,--format=csv -n 8 -s 42 --use-p2p 8,9139620,217610,42,0,0,-1,0,0,,10000021,7311680,--format=csv -n 8 -s 42 8,15563635,361945,43,1,0,-1,0,0,,10000003,12450904,--format=csv -n 8 -s 43 --use-p2p 8,9342567,217269,43,1,0,-1,0,0,,10000021,7474037,--format=csv -n 8 -s 43 8,16707108,379707,44,2,0,-1,0,0,,10000005,13365679,--format=csv -n 8 -s 44 --use-p2p 8,9534712,216698,44,2,0,-1,0,0,,10000000,7627769,--format=csv -n 8 -s 44 8,17458288,379528,46,4,0,-1,0,0,,10000000,13966630,--format=csv -n 8 -s 46 --use-p2p 8,9972294,216789,46,4,0,-1,0,0,,10000017,7977821,--format=csv -n 8 -s 46 8,19030400,380608,50,8,0,22,22,653,,10000018,15224292,--format=csv -n 8 -s 50 --use-p2p 8,10814750,216295,50,8,0,41,42,3095,,10000015,8651787,--format=csv -n 8 -s 50 8,22049280,380160,58,16,0,22,22,3021,,10000012,17639402,--format=csv -n 8 -s 58 --use-p2p 8,12556072,216484,58,16,0,41,42,234,,10000030,10044827,--format=csv -n 8 -s 58 8,28167360,380640,74,32,0,22,22,850,,10000020,22533842,--format=csv -n 8 -s 74 --use-p2p 8,16076426,217249,74,32,0,41,42,635,,10000006,12861133,--format=csv -n 8 -s 74 8,40206330,379305,106,64,0,22,23,7066,,10000018,32165006,--format=csv -n 8 -s 106 --use-p2p 8,22943382,216447,106,64,0,41,42,620,,10000035,18354641,--format=csv -n 8 -s 106 8,64253370,377961,170,128,0,22,23,1730,,10000019,51402598,--format=csv -n 8 -s 170 --use-p2p 8,36731730,216069,170,128,0,41,42,500,,10000021,29385322,--format=csv -n 8 -s 170 8,112488742,377479,298,256,0,22,23,601,,10000021,89990804,--format=csv -n 8 -s 298 --use-p2p 8,64457102,216299,298,256,0,41,42,491,,10000041,51565470,--format=csv -n 8 -s 298 8,208162730,375745,554,512,0,22,23,362,,10000016,166529917,--format=csv -n 8 -s 554 --use-p2p 8,119535472,215768,554,512,0,41,42,988,,10000033,95628062,--format=csv -n 8 -s 554 8,393959488,369568,1066,1024,0,23,23,6760,,10000013,315167180,--format=csv -n 8 -s 1066 --use-p2p 8,226235048,212228,1066,1024,0,42,43,1049,,10000017,180987730,--format=csv -n 8 -s 1066 8,547935000,365290,1500,1458,0,23,23,2717,,10000007,438347693,--format=csv -n 8 -s 1500 --use-p2p 8,317413500,211609,1500,1458,0,42,43,1088,,10000004,253930698,--format=csv -n 8 -s 1500 12,12142116,289098,42,0,0,-1,0,0,,10000007,9713686,--format=csv -n 12 -s 42 --use-p2p 12,6481104,154312,42,0,0,-1,0,0,,10000011,5184877,--format=csv -n 12 -s 42 12,12338764,286948,43,1,0,-1,0,0,,10000018,9870993,--format=csv -n 12 -s 43 --use-p2p 12,6580247,153029,43,1,0,-1,0,0,,10000019,5264187,--format=csv -n 12 -s 43 12,12567984,285636,44,2,0,-1,0,0,,10000019,10054368,--format=csv -n 12 -s 44 --use-p2p 12,6736620,153105,44,2,0,-1,0,0,,10000043,5389272,--format=csv -n 12 -s 44 12,13163498,286163,46,4,0,-1,0,0,,10000005,10530793,--format=csv -n 12 -s 46 --use-p2p 12,7046372,153182,46,4,0,-1,0,0,,10000043,5637073,--format=csv -n 12 -s 46 12,13963400,279268,50,8,0,30,31,1661,,10000013,11170705,--format=csv -n 12 -s 50 --use-p2p 12,7619550,152391,50,8,0,59,61,2668,,10000044,6095613,--format=csv -n 12 -s 50 12,16665720,287340,58,16,0,30,31,621,,10000012,13332560,--format=csv -n 12 -s 58 --use-p2p 12,8873188,152986,58,16,0,59,61,1263,,10000004,7098547,--format=csv -n 12 -s 58 12,21206550,286575,74,32,0,30,31,7075,,10000022,16965202,--format=csv -n 12 -s 74 --use-p2p 12,11318818,152957,74,32,0,59,61,618,,10000046,9055012,--format=csv -n 12 -s 74 12,30329886,286131,106,64,0,30,31,6473,,10000027,24263843,--format=csv -n 12 -s 106 --use-p2p 12,16024762,151177,106,64,0,60,61,2854,,10000046,12819750,--format=csv -n 12 -s 106 12,48653150,286195,170,128,0,30,31,1298,,10000022,38922434,--format=csv -n 12 -s 170 --use-p2p 12,25947100,152630,170,128,0,60,61,1705,,10000020,20757638,--format=csv -n 12 -s 170 12,84861162,284769,298,256,0,30,31,625,,10000020,67888793,--format=csv -n 12 -s 298 --use-p2p 12,45321032,152084,298,256,0,59,61,860,,10000034,36256702,--format=csv -n 12 -s 298 12,157378658,284077,554,512,0,31,31,1792,,10000027,125902586,--format=csv -n 12 -s 554 --use-p2p 12,83980860,151590,554,512,0,60,61,1616,,10000006,67184647,--format=csv -n 12 -s 554 12,286565318,268823,1066,1024,0,31,32,1986,,10000018,229251841,--format=csv -n 12 -s 1066 --use-p2p 12,159903198,150003,1066,1024,0,60,62,619,,10000028,127922200,--format=csv -n 12 -s 1066 12,418357500,278905,1500,1458,0,31,32,635,,10000020,334685330,--format=csv -n 12 -s 1500 --use-p2p 12,223633500,149089,1500,1458,0,61,62,618,,10000044,178906012,--format=csv -n 12 -s 1500 16,9582594,228157,42,0,0,-1,0,0,,10000035,7666048,--format=csv -n 16 -s 42 --use-p2p 16,4931850,117425,42,0,0,-1,0,0,,10000065,3945454,--format=csv -n 16 -s 42 16,9784521,227547,43,1,0,-1,0,0,,10000030,7827593,--format=csv -n 16 -s 43 --use-p2p 16,5027560,116920,43,1,0,-1,0,0,,10000022,4022039,--format=csv -n 16 -s 43 16,10029008,227932,44,2,0,-1,0,0,,10000013,8023195,--format=csv -n 16 -s 44 --use-p2p 16,5088688,115652,44,2,0,-1,0,0,,10000008,4070947,--format=csv -n 16 -s 44 16,10544764,229234,46,4,0,-1,0,0,,10000016,8435797,--format=csv -n 16 -s 46 --use-p2p 16,5343590,116165,46,4,0,-1,0,0,,10000069,4274842,--format=csv -n 16 -s 46 16,11156150,223123,50,8,0,39,40,2095,,10000009,8924911,--format=csv -n 16 -s 50 --use-p2p 16,5805850,116117,50,8,0,79,81,2000,,10000019,4644671,--format=csv -n 16 -s 50 16,13174468,227146,58,16,0,39,40,3043,,10000006,10539568,--format=csv -n 16 -s 58 --use-p2p 16,6746618,116321,58,16,0,78,80,2045,,10000027,5397279,--format=csv -n 16 -s 58 16,16855572,227778,74,32,0,39,40,299,,10000001,13484456,--format=csv -n 16 -s 74 --use-p2p 16,8541228,115422,74,32,0,78,81,1311,,10000008,6832976,--format=csv -n 16 -s 74 16,24361980,229830,106,64,0,38,39,362,,10000031,19489523,--format=csv -n 16 -s 106 --use-p2p 16,12362568,116628,106,64,0,78,80,2020,,10000008,9890046,--format=csv -n 16 -s 106 16,38781250,228125,170,128,0,39,40,2131,,10000039,31024879,--format=csv -n 16 -s 170 --use-p2p 16,19882180,116954,170,128,0,78,80,1062,,10000072,15905629,--format=csv -n 16 -s 170 16,68188956,228822,298,256,0,39,39,646,,10000012,54551099,--format=csv -n 16 -s 298 --use-p2p 16,34508996,115802,298,256,0,78,81,2560,,10000072,27606998,--format=csv -n 16 -s 298 16,119657352,215988,554,512,0,39,40,3023,,10000036,95725536,--format=csv -n 16 -s 554 --use-p2p 16,63005312,113728,554,512,0,78,80,2676,,10000048,50404007,--format=csv -n 16 -s 554 16,233278110,218835,1066,1024,0,39,40,5541,,10000040,186621741,--format=csv -n 16 -s 1066 --use-p2p 16,122727514,115129,1066,1024,0,79,81,2908,,10000045,98181569,--format=csv -n 16 -s 1066 16,327915000,218610,1500,1458,0,39,40,4895,,10000025,262331344,--format=csv -n 16 -s 1500 --use-p2p 16,171390000,114260,1500,1458,0,79,81,635,,10000047,137111355,--format=csv -n 16 -s 1500 32,5328162,126861,42,0,0,-1,0,0,,10000043,4262511,--format=csv -n 32 -s 42 --use-p2p 32,2586570,61585,42,0,0,-1,0,0,,10000064,2069242,--format=csv -n 32 -s 42 32,5343911,124277,43,1,0,-1,0,0,,10000055,4275105,--format=csv -n 32 -s 43 --use-p2p 32,2678513,62291,43,1,0,-1,0,0,,10000110,2142786,--format=csv -n 32 -s 43 32,5521736,125494,44,2,0,-1,0,0,,10000007,4417385,--format=csv -n 32 -s 44 --use-p2p 32,2728484,62011,44,2,0,-1,0,0,,10000073,2182771,--format=csv -n 32 -s 44 32,5730588,124578,46,4,0,-1,0,0,,10000012,4584464,--format=csv -n 32 -s 46 --use-p2p 32,2860004,62174,46,4,0,-1,0,0,,10000127,2287974,--format=csv -n 32 -s 46 32,6203950,124079,50,8,0,72,74,779,,10000018,4963151,--format=csv -n 32 -s 50 --use-p2p 32,3092200,61844,50,8,0,153,157,2353,,10000152,2473722,--format=csv -n 32 -s 50 32,7210618,124321,58,16,0,72,73,2468,,10000056,5768462,--format=csv -n 32 -s 58 --use-p2p 32,3585618,61821,58,16,0,153,157,3066,,10000134,2868455,--format=csv -n 32 -s 58 32,9131156,123394,74,32,0,72,74,731,,10000004,7304921,--format=csv -n 32 -s 74 --use-p2p 32,4598138,62137,74,32,0,153,157,2098,,10000122,3678465,--format=csv -n 32 -s 74 32,13017542,122807,106,64,0,72,74,1982,,10000024,10414008,--format=csv -n 32 -s 106 --use-p2p 32,6599136,62256,106,64,0,153,156,715,,10000139,5279235,--format=csv -n 32 -s 106 32,21003330,123549,170,128,0,72,73,5149,,10000052,16802576,--format=csv -n 32 -s 170 --use-p2p 32,10559380,62114,170,128,0,153,157,1893,,10000060,8447453,--format=csv -n 32 -s 170 32,36544038,122631,298,256,0,72,74,1727,,10000061,29235052,--format=csv -n 32 -s 298 --use-p2p 32,18435472,61864,298,256,0,154,157,945,,10000044,14748312,--format=csv -n 32 -s 298 32,66123224,119356,554,512,0,72,74,2657,,10000053,52898298,--format=csv -n 32 -s 554 --use-p2p 32,33541376,60544,554,512,0,154,157,3166,,10000000,26833100,--format=csv -n 32 -s 554 32,130040274,121989,1066,1024,0,73,74,2579,,10000103,104031147,--format=csv -n 32 -s 1066 --use-p2p 32,65690118,61623,1066,1024,0,154,158,1183,,10000025,52551963,--format=csv -n 32 -s 1066 32,181837500,121225,1500,1458,0,73,75,6909,,10000054,145469214,--format=csv -n 32 -s 1500 --use-p2p 32,92371500,61581,1500,1458,0,154,158,2651,,10000096,73896490,--format=csv -n 32 -s 1500 64,2659860,63330,42,0,0,-1,0,0,,10000020,2127883,--format=csv -n 64 -s 42 --use-p2p 64,1298850,30925,42,0,0,-1,0,0,,10000297,1039049,--format=csv -n 64 -s 42 64,2617969,60883,43,1,0,-1,0,0,,10000016,2094371,--format=csv -n 64 -s 43 --use-p2p 64,1284152,29864,43,1,0,-1,0,0,,10000207,1027300,--format=csv -n 64 -s 43 64,2661032,60478,44,2,0,-1,0,0,,10000022,2128820,--format=csv -n 64 -s 44 --use-p2p 64,1353220,30755,44,2,0,-1,0,0,,10000271,1082546,--format=csv -n 64 -s 44 64,2779550,60425,46,4,0,-1,0,0,,10000061,2223626,--format=csv -n 64 -s 46 --use-p2p 64,1412476,30706,46,4,0,-1,0,0,,10000020,1129978,--format=csv -n 64 -s 46 64,2966000,59320,50,8,0,145,150,3123,,10000132,2372768,--format=csv -n 64 -s 50 --use-p2p 64,1497600,29952,50,8,0,313,322,3161,,10000064,1198072,--format=csv -n 64 -s 50 64,3410168,58796,58,16,0,146,150,2262,,10000100,2728107,--format=csv -n 64 -s 58 --use-p2p 64,1786632,30804,58,16,0,312,320,1095,,10000072,1429295,--format=csv -n 64 -s 58 64,4303544,58156,74,32,0,145,151,1590,,10000149,3442783,--format=csv -n 64 -s 74 --use-p2p 64,2254336,30464,74,32,0,315,323,1907,,10000301,1803414,--format=csv -n 64 -s 74 64,5980202,56417,106,64,0,147,152,3112,,10000063,4784131,--format=csv -n 64 -s 106 --use-p2p 64,3250490,30665,106,64,0,313,321,1081,,10000163,2600349,--format=csv -n 64 -s 106 64,9245960,54388,170,128,0,146,151,2991,,10000050,7396731,--format=csv -n 64 -s 170 --use-p2p 64,5216450,30685,170,128,0,313,321,2447,,10000028,4173148,--format=csv -n 64 -s 170 64,16653134,55883,298,256,0,148,152,1069,,10000129,13322335,--format=csv -n 64 -s 298 --use-p2p 64,9130720,30640,298,256,0,314,321,2367,,10000222,7304413,--format=csv -n 64 -s 298 64,30402966,54879,554,512,0,148,153,3068,,10000146,24322017,--format=csv -n 64 -s 554 --use-p2p 64,16951292,30598,554,512,0,314,322,2139,,10000197,13560766,--format=csv -n 64 -s 554 64,56105712,52632,1066,1024,0,148,154,2721,,10000069,44884259,--format=csv -n 64 -s 1066 --use-p2p 64,32371222,30367,1066,1024,0,315,324,2221,,10000312,25896169,--format=csv -n 64 -s 1066 64,74034000,49356,1500,1458,0,147,153,7388,,10000104,59226584,--format=csv -n 64 -s 1500 --use-p2p 64,45405000,30270,1500,1458,0,317,326,1971,,10000323,36322826,--format=csv -n 64 -s 1500 nemu-0.3.1/benchmarks/preliminar/results-simu.txt000066400000000000000000000153161300327754000221620ustar00rootroot00000000000000# ns3user-ns3kernel # pktsz nodes usec mem 1400.0 1.0 11.6054 308000.0 exp1 1000.0 1.0 11.1844 308000.0 exp1 600.0 1.0 10.3422 308000.0 exp1 400.0 1.0 10.2632 308000.0 exp1 100.0 1.0 9.82896 308000.0 exp1 80.0 1.0 9.85264 308000.0 exp1 30.0 1.0 9.63947 308000.0 exp1 10.0 1.0 9.63816 308000.0 exp1 1400.0 2.0 15.4739 344000.0 exp1 1000.0 2.0 14.6055 344000.0 exp1 600.0 2.0 14.0527 344000.0 exp1 400.0 2.0 13.8948 344000.0 exp1 100.0 2.0 13.3948 344000.0 exp1 80.0 2.0 13.4211 344000.0 exp1 30.0 2.0 16.2884 344000.0 exp1 10.0 2.0 27.215 344000.0 exp1 1400.0 3.0 22.4744 356000.0 exp1 1000.0 3.0 21.8427 356000.0 exp1 600.0 3.0 20.8424 356000.0 exp1 400.0 3.0 21.0529 356000.0 exp1 100.0 3.0 20.4737 356000.0 exp1 80.0 3.0 20.3264 356000.0 exp1 30.0 3.0 23.3369 356000.0 exp1 10.0 3.0 34.38 356000.0 exp1 1400.0 4.0 29.2904 364000.0 exp1 1000.0 4.0 28.2902 364000.0 exp1 600.0 4.0 28.1056 364000.0 exp1 400.0 4.0 27.6845 364000.0 exp1 100.0 4.0 27.3948 364000.0 exp1 80.0 4.0 27.1053 364000.0 exp1 30.0 4.0 30.0064 364000.0 exp1 10.0 4.0 40.6451 364000.0 exp1 1400.0 10.0 69.0849 424000.0 exp1 1000.0 10.0 69.7414 424000.0 exp1 600.0 10.0 68.6083 424000.0 exp1 400.0 10.0 68.5285 424000.0 exp1 100.0 10.0 66.027 424000.0 exp1 80.0 10.0 66.8427 424000.0 exp1 30.0 10.0 69.9604 424000.0 exp1 10.0 10.0 80.4004 424000.0 exp1 1400.0 20.0 136.711 528000.0 exp1 1000.0 20.0 137.915 528000.0 exp1 600.0 20.0 138.17 528000.0 exp1 400.0 20.0 146.536 528000.0 exp1 100.0 20.0 134.371 528000.0 exp1 80.0 20.0 136.676 528000.0 exp1 30.0 20.0 135.614 528000.0 exp1 10.0 20.0 146.406 528000.0 exp1 # posixuser-ns3kernel # pktsz nodes usec mem 1400.0 1.0 23.9935 284000.0 exp2 1000.0 1.0 22.275 284000.0 exp2 600.0 1.0 21.1502 284000.0 exp2 400.0 1.0 20.3162 284000.0 exp2 100.0 1.0 19.3629 284000.0 exp2 80.0 1.0 17.2199 284000.0 exp2 30.0 1.0 11.6907 284000.0 exp2 10.0 1.0 9.41529 284000.0 exp2 1400.0 2.0 29.8507 320000.0 exp2 1000.0 2.0 28.3504 320000.0 exp2 600.0 2.0 26.4979 320000.0 exp2 400.0 2.0 25.6115 320000.0 exp2 100.0 2.0 24.1463 320000.0 exp2 80.0 2.0 23.0901 320000.0 exp2 30.0 2.0 23.7398 320000.0 exp2 10.0 2.0 34.3538 320000.0 exp2 1400.0 3.0 41.754 332000.0 exp2 1000.0 3.0 40.6361 332000.0 exp2 600.0 3.0 37.2756 332000.0 exp2 400.0 3.0 35.2836 332000.0 exp2 100.0 3.0 34.4021 332000.0 exp2 80.0 3.0 32.5921 332000.0 exp2 30.0 3.0 32.4704 332000.0 exp2 10.0 3.0 42.9911 332000.0 exp2 1400.0 4.0 50.8228 340000.0 exp2 1000.0 4.0 48.7364 340000.0 exp2 600.0 4.0 49.6738 340000.0 exp2 400.0 4.0 41.5514 340000.0 exp2 100.0 4.0 41.3879 340000.0 exp2 80.0 4.0 40.0402 340000.0 exp2 30.0 4.0 39.5275 340000.0 exp2 10.0 4.0 50.0416 340000.0 exp2 1400.0 10.0 105.808 400000.0 exp2 1000.0 10.0 100.717 400000.0 exp2 600.0 10.0 92.6241 400000.0 exp2 400.0 10.0 88.9403 400000.0 exp2 100.0 10.0 84.3573 400000.0 exp2 80.0 10.0 83.9941 400000.0 exp2 30.0 10.0 82.3179 400000.0 exp2 10.0 10.0 90.7838 400000.0 exp2 1400.0 20.0 195.386 500000.0 exp2 1000.0 20.0 187.948 500000.0 exp2 600.0 20.0 174.074 500000.0 exp2 400.0 20.0 161.676 500000.0 exp2 100.0 20.0 158.474 500000.0 exp2 80.0 20.0 153.947 500000.0 exp2 30.0 20.0 149.705 500000.0 exp2 10.0 20.0 158.209 500000.0 exp2 # posixuser-linuxkernel 1400.0 1.0 18.8925 1804000.0 exp3-nopic 1000.0 1.0 17.415 1804000.0 exp3-nopic 600.0 1.0 15.4777 1804000.0 exp3-nopic 400.0 1.0 14.967 1804000.0 exp3-nopic 100.0 1.0 13.9716 1804000.0 exp3-nopic 80.0 1.0 11.4151 1804000.0 exp3-nopic 30.0 1.0 5.42379 1804000.0 exp3-nopic 10.0 1.0 3.0551 1804000.0 exp3-nopic 1400.0 2.0 35.1408 2312000.0 exp3-nopic 1000.0 2.0 33.6159 2312000.0 exp3-nopic 600.0 2.0 30.5503 2312000.0 exp3-nopic 400.0 2.0 29.2868 2312000.0 exp3-nopic 100.0 2.0 27.6743 2312000.0 exp3-nopic 80.0 2.0 25.5777 2312000.0 exp3-nopic 30.0 2.0 25.6222 2312000.0 exp3-nopic 10.0 2.0 34.1143 2312000.0 exp3-nopic 1400.0 3.0 50.8247 3408000.0 exp3-nopic 1000.0 3.0 48.469 3408000.0 exp3-nopic 600.0 3.0 44.0047 3408000.0 exp3-nopic 400.0 3.0 41.6631 3408000.0 exp3-nopic 100.0 3.0 39.9732 3408000.0 exp3-nopic 80.0 3.0 38.92 3408000.0 exp3-nopic 30.0 3.0 38.1046 3408000.0 exp3-nopic 10.0 3.0 46.4725 3408000.0 exp3-nopic 1400.0 4.0 67.4539 4508000.0 exp3-nopic 1000.0 4.0 63.3236 4508000.0 exp3-nopic 600.0 4.0 58.0279 4508000.0 exp3-nopic 400.0 4.0 55.6078 4508000.0 exp3-nopic 100.0 4.0 53.1384 4508000.0 exp3-nopic 80.0 4.0 51.3447 4508000.0 exp3-nopic 30.0 4.0 50.6402 4508000.0 exp3-nopic 10.0 4.0 59.1352 4508000.0 exp3-nopic 1400.0 10.0 160.839 11100000.0 exp3-nopic 1000.0 10.0 162.478 11100000.0 exp3-nopic 600.0 10.0 139.116 11100000.0 exp3-nopic 400.0 10.0 136.227 11100000.0 exp3-nopic 100.0 10.0 132.659 11100000.0 exp3-nopic 80.0 10.0 128.236 11100000.0 exp3-nopic 30.0 10.0 122.553 11100000.0 exp3-nopic 10.0 10.0 133.47 11100000.0 exp3-nopic 1400.0 20.0 311.62 22096000.0 exp3-nopic 1000.0 20.0 301.081 22096000.0 exp3-nopic 600.0 20.0 276.518 22096000.0 exp3-nopic 400.0 20.0 269.517 22096000.0 exp3-nopic 100.0 20.0 259.321 22096000.0 exp3-nopic 80.0 20.0 258.255 22096000.0 exp3-nopic 30.0 20.0 236.266 22096000.0 exp3-nopic 10.0 20.0 257.22 22096000.0 exp3-nopic 1400.0 1.0 20.7818 1108000.0 exp3-pic 1000.0 1.0 17.685 1108000.0 exp3-pic 600.0 1.0 16.4502 1108000.0 exp3-pic 400.0 1.0 15.5613 1108000.0 exp3-pic 100.0 1.0 14.3905 1108000.0 exp3-pic 80.0 1.0 11.9988 1108000.0 exp3-pic 30.0 1.0 5.92644 1108000.0 exp3-pic 10.0 1.0 3.4402 1108000.0 exp3-pic 1400.0 2.0 36.6522 832000.0 exp3-pic 1000.0 2.0 34.561 832000.0 exp3-pic 600.0 2.0 32.5762 832000.0 exp3-pic 400.0 2.0 30.2595 832000.0 exp3-pic 100.0 2.0 29.2824 832000.0 exp3-pic 80.0 2.0 27.0263 832000.0 exp3-pic 30.0 2.0 26.965 832000.0 exp3-pic 10.0 2.0 36.4201 832000.0 exp3-pic 1400.0 3.0 53.6588 1184000.0 exp3-pic 1000.0 3.0 51.5742 1184000.0 exp3-pic 600.0 3.0 47.6515 1184000.0 exp3-pic 400.0 3.0 44.6892 1184000.0 exp3-pic 100.0 3.0 43.6219 1184000.0 exp3-pic 80.0 3.0 41.2227 1184000.0 exp3-pic 30.0 3.0 40.3945 1184000.0 exp3-pic 10.0 3.0 49.6258 1184000.0 exp3-pic 1400.0 4.0 69.3434 1536000.0 exp3-pic 1000.0 4.0 66.294 1536000.0 exp3-pic 600.0 4.0 61.6749 1536000.0 exp3-pic 400.0 4.0 58.526 1536000.0 exp3-pic 100.0 4.0 56.5305 1536000.0 exp3-pic 80.0 4.0 54.8369 1536000.0 exp3-pic 30.0 4.0 54.085 1536000.0 exp3-pic 10.0 4.0 64.7179 1536000.0 exp3-pic 1400.0 10.0 164.997 3668000.0 exp3-pic 1000.0 10.0 157.48 3668000.0 exp3-pic 600.0 10.0 145.926 3668000.0 exp3-pic 400.0 10.0 142.443 3668000.0 exp3-pic 100.0 10.0 137.499 3668000.0 exp3-pic 80.0 10.0 136.347 3668000.0 exp3-pic 30.0 10.0 126.181 3668000.0 exp3-pic 10.0 10.0 139.126 3668000.0 exp3-pic 1400.0 20.0 321.641 7224000.0 exp3-pic 1000.0 20.0 309.865 7224000.0 exp3-pic 600.0 20.0 290.551 7224000.0 exp3-pic 400.0 20.0 282.715 7224000.0 exp3-pic 100.0 20.0 272.982 7224000.0 exp3-pic 80.0 20.0 266.403 7224000.0 exp3-pic 30.0 20.0 238.415 7224000.0 exp3-pic 10.0 20.0 271.268 7224000.0 exp3-pic nemu-0.3.1/benchmarks/udp-perf.c000066400000000000000000000272621300327754000164740ustar00rootroot00000000000000/* vim: ts=4:sw=4:et:ai:sts=4 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define HDR_SIZE (14 + 20 + 8) /* eth + ip + udp headers */ static uint64_t current_time(void); static void fatal(const char *func, const char *detailed) { char *error; if(detailed) fprintf(stderr, "%s\n", detailed); if(func) { error = strerror(errno); fprintf(stderr, "%s: %s (%d)\n", func, error, errno); } exit(1); } static void set_txbuf_size(int fd, int buffer_size) { int msg_size = buffer_size; int status = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char*)&msg_size, sizeof(msg_size)); if(status == -1) fatal("setsockopt", "Unable to set socket buffer size"); } static void run_client(const char *to_ip, unsigned to_port, unsigned pkt_size) { int status, fd, cfd; uint64_t seq; struct sockaddr_in addr, to; void *buffer; if(pkt_size < HDR_SIZE) fatal(NULL, "Cannot send packets that small."); pkt_size -= HDR_SIZE; buffer = malloc(pkt_size); if(! buffer) fatal("malloc", NULL); memset(buffer, 0, pkt_size); fd = socket(AF_INET, SOCK_DGRAM, 0); if(fd == -1) fatal("socket", "Unable to create udp socket"); cfd = socket(AF_INET, SOCK_STREAM, 0); if(cfd == -1) fatal("socket", "Unable to create tcp socket"); #if 0 status = fcntl(fd, F_GETFL, 0); if(status == -1) fatal("fcntl", NULL); status = fcntl(fd, F_SETFL, status | O_NONBLOCK); if(status == -1) fatal("fcntl", NULL); set_txbuf_size(fd, 1<<20); #endif addr.sin_family = AF_INET; addr.sin_port = 0; addr.sin_addr.s_addr = htonl(INADDR_ANY); to.sin_family = AF_INET; to.sin_port = htons(to_port); to.sin_addr.s_addr = inet_addr(to_ip); status = bind(fd, (struct sockaddr*)&addr, sizeof(addr)); if(status == -1) fatal("bind", NULL); status = connect(cfd, (struct sockaddr*)&to, sizeof(to)); if(status == -1) fatal("connect", "Can not connect to server"); for(seq = 0; ; seq++) { ssize_t written; uint64_t now; fd_set rfds; struct timeval tv; /* Check if asked to stop */ FD_ZERO(&rfds); FD_SET(cfd, &rfds); tv.tv_sec = 0; tv.tv_usec = 0; status = select(cfd + 1, &rfds, NULL, NULL, &tv); if(status == -1) fatal("select", NULL); if(status) { status = recv(cfd, buffer, 8, 0); if(status == -1) fatal("recv", NULL); if(((uint64_t *)buffer)[0] == 0xdeadbeef) break; fatal(NULL, "Received invalid control message"); } now = current_time(); if(pkt_size >= sizeof(uint64_t)) ((uint64_t *)buffer)[0] = now; if(pkt_size >= 2 * sizeof(uint64_t)) ((uint64_t *)buffer)[1] = seq; written = sendto(fd, buffer, pkt_size, 0, (struct sockaddr *)&to, sizeof(to)); if(written == -1 && (errno == EWOULDBLOCK || errno == EAGAIN)) continue; if(written == -1) fatal("sendto", NULL); } free(buffer); status = close(cfd); if(status == -1) fatal("close", "Unable to close socket"); status = close(fd); if(status == -1) fatal("close", "Unable to close socket"); } static uint64_t current_time(void) { struct timeval tv; uint64_t current_time; int status; status = gettimeofday(&tv, 0); if(status == -1) fatal("gettimeofday", "Unable to get current time\n"); current_time = tv.tv_sec * 1000000 + tv.tv_usec; return current_time; } static void run_server(int port, uint64_t max_time, uint64_t max_pkts, uint64_t max_bytes, bool verbose) { struct sockaddr_in addr; int fd, cfd, serverfd, status; uint64_t now, last_ts, last_seq, preceived, breceived, errors; uint64_t start, tot_delay, max_delay, min_delay, last_delay; double jitter = 0.0L; ssize_t pkt_size = -1, buffer_sz = 1 << 17; /* should be enough */ void *buffer; uint64_t magic = 0xdeadbeef; buffer = malloc(buffer_sz); if(! buffer) fatal("malloc", NULL); fd = socket(AF_INET, SOCK_DGRAM, 0); if(fd == -1) fatal("socket", "Unable to create udp socket"); serverfd = socket(AF_INET, SOCK_STREAM, 0); if(serverfd == -1) fatal("socket", "Unable to create tcp socket"); addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = htonl(INADDR_ANY); status = bind(fd,(struct sockaddr*)&addr, sizeof(addr)); if(status == -1) fatal("bind", "Unable to bind to specified port"); status = 1; /* no need for other var :) */ status = setsockopt(serverfd, SOL_SOCKET, SO_REUSEADDR, &status, sizeof(status)); if(status == -1) fatal("setsockopt", "Unable to set SO_REUSEADDR"); status = bind(serverfd,(struct sockaddr*)&addr, sizeof(addr)); if(status == -1) fatal("bind", "Unable to bind to specified port"); status = listen(serverfd, 1); if(status == -1) fatal("listen", "Unable to receive connections"); cfd = accept(serverfd, NULL, 0); if(cfd == -1) fatal("accept", "Unable to receive connection"); preceived = breceived = errors = 0; last_ts = last_seq = start = 0; tot_delay = max_delay = 0; last_delay = min_delay = -1; while(true) { uint64_t ts = 0, seq = 0; ssize_t received; received = recvfrom(fd, buffer, buffer_sz, 0, 0, 0); now = current_time(); if(received >= sizeof(uint64_t)) ts = ((uint64_t *)buffer)[0]; if(received >= 2 * sizeof(uint64_t)) seq = ((uint64_t *)buffer)[1]; if(pkt_size == -1) { /* init: first packet is ignored */ pkt_size = received; last_ts = ts; last_seq = seq; start = now; } else { if(pkt_size != received) { errors++; fprintf(stderr, "Received packet of invalid size %ld.\n", received); } else { preceived++; breceived += received; breceived += HDR_SIZE; if(ts) { if(last_delay >= 0) { double delta; if(last_delay > now - ts) delta = last_delay - (now - ts); else delta = (now - ts) - last_delay; jitter += (delta - jitter) / 16.0; } last_delay = now - ts; tot_delay += last_delay; if(last_delay < min_delay) min_delay = last_delay; if(last_delay > max_delay) max_delay = last_delay; } if((ts && ts <= last_ts) || (seq && seq <= last_seq)) { errors++; fprintf(stderr, "Packet received out of order.\n"); } last_ts = ts; last_seq = seq; } if((max_pkts && preceived + errors >= max_pkts) || (max_time && now - start >= max_time) || (max_bytes && breceived >= max_bytes)) break; } } free(buffer); /* Tell client to die */ send(cfd, &magic, sizeof(magic), 0); status = close(cfd); if(status == -1) fatal("close", "Unable to close socket"); status = close(serverfd); if(status == -1) fatal("close", "Unable to close socket"); if(verbose) { printf("Received: %ld bytes %ld packets (size %ld/%ld) %ld errors.\n", breceived, preceived, pkt_size + HDR_SIZE, pkt_size, errors); printf("Delay: %ld/%ld/%ld (min/avg/max). Jitter: %lf. Time: %ld us\n", min_delay, tot_delay / preceived, max_delay, jitter, now - start); printf("Bandwidth: %ld bit/s.\n", (long)(1.0L * (breceived * 8000000) / (now - start))); } else { printf("brx:%ld prx:%ld pksz:%ld plsz:%ld err:%ld ", breceived, preceived, pkt_size + HDR_SIZE, pkt_size, errors); printf("mind:%ld avgd:%ld maxd:%ld jit:%lf time:%ld ", min_delay, tot_delay / preceived, max_delay, jitter, now - start); } status = close(fd); if(status == -1) fatal("close", "Unable to close socket"); } #define CHECK_INT_ARG(arg, name, value) \ if(strncmp(arg, "--"name"=", strlen("--"name"=")) == 0) { \ value = atoi(arg + strlen("--"name"=")); \ continue; \ } #define CHECK_LLINT_ARG(arg, name, value) \ if(strncmp(arg, "--"name"=", strlen("--"name"=")) == 0) { \ value = atoll(arg + strlen("--"name"=")); \ continue; \ } #define CHECK_STR_ARG(arg, name, value) \ if(strncmp(arg, "--"name"=", strlen("--"name"=")) == 0) { \ value = arg + strlen("--"name"="); \ continue; \ } static char *progname; void usage(FILE *f) { char *filler, *sp = " "; if(strlen(progname) < strlen(sp)) filler = sp + strlen(sp) - strlen(progname); else filler = sp; fprintf(f, "\n"); fprintf(f, "Usage: %s --client [--host=HOST] [--port=PORT] " "[--pktsize=BYTES]\n", progname); fprintf(f, " %s --server [--port=PORT] [--max-time=SECS] " "[--max-pkts=NUM]\n", progname); fprintf(f, " %s [--max-bytes=BYTES] [--verbose]\n", filler); } int main(int argc, char *argv[]) { uint64_t max_time = 0, max_pkts = 0, max_bytes = 0; int pkt_size = 1500, port = 5000; const char *to_ip = "127.0.0.1"; bool server = false, client = false, verbose = false; char **arg = argv + 1; progname = strrchr(argv[0], '/'); if(progname) progname++; /* skip over the slash */ else progname = argv[0]; for(; *arg != 0; arg++) { CHECK_INT_ARG(*arg, "pktsize", pkt_size); CHECK_INT_ARG(*arg, "port", port); CHECK_LLINT_ARG(*arg, "max-time", max_time); CHECK_LLINT_ARG(*arg, "max-pkts", max_pkts); CHECK_LLINT_ARG(*arg, "max-bytes", max_bytes); CHECK_STR_ARG(*arg, "host", to_ip); if(strcmp(*arg, "--server") == 0) { server = true; continue; } if(strcmp(*arg, "--client") == 0) { client = true; continue; } if(strcmp(*arg, "--verbose") == 0) { verbose = true; continue; } if(strcmp(*arg, "--help") == 0) { usage(stdout); exit(0); } fprintf(stderr, "Unknown parameter: %s\n", *arg); usage(stderr); exit(1); } if(client == server) { fprintf(stderr, "Exactly one of --client and --server must be specified.\n"); usage(stderr); exit(1); } if(!(max_time || max_pkts || max_bytes)) max_time = 10; max_time *= 1000000; if(client) run_client(to_ip, port, pkt_size); else run_server(port, max_time, max_pkts, max_bytes, verbose); return 0; } nemu-0.3.1/docs/000077500000000000000000000000001300327754000134105ustar00rootroot00000000000000nemu-0.3.1/docs/X11Forwarding.md000066400000000000000000000037131300327754000163320ustar00rootroot00000000000000# Support for X11 forwarding in Nemu ## Introduction Nemu has special support for forwarding X11 sessions, so interactive programs can be run from inside virtual nodes. When running in a different network namespace (a virtual node), UNIX domain sockets cannot be used to communicate to the main namespace, or with other namespaces. Because of this, the usual way to start X11 applications does not work. Furthermore, as the network topology is constructed by the user, and can be completely arbitrary, in general there is no guarantee that normal X11-over-TCP would work either! To solve this problem, Nemu starts a separate process per virtual node that will forward TCP connections inside the node to the local X11 UNIX domain or TCP socket, in a very similar way to what SSH does to forward X11 connections. A nice UNIX trick is used to pass the listening socket to the virtual node while keeping the other end in the "normal" main namespace (see http://pypi.python.org/pypi/python-passfd). ## Troubleshooting To enable this feature, create your `nemu.Node` object with the optional argument `forward_X11` set to `True`. Note that when switching to root to run your Nemu scripts, it could happen that the X11 libraries are not able to obtain the proper credentials to connect to your display: $ sudo xeyes Error: Can't open display: localhost:11.0 This is usually seen when using forwarded X11 connections through SSH, as it does not set the `XAUTHORITY` variable, and the libraries take the default value, which is different for you and for the root user. As a simple workaround, you can set the `XAUTHORITY` variable to the full path of your xauth file (sudo keeps `XAUTHORITY` unchanged): $ XAUTHORITY=${HOME}/.Xauthority sudo xeyes This will not work in most NFS-mounted home directories, you will need to copy the xauth file out of NFS first: $ cp ${HOME}/.Xauthority /tmp/${USER}-Xauthority $ export XAUTHORITY=/tmp/${USER}-Xauthority $ sudo xeyes nemu-0.3.1/docs/debconf-talk/000077500000000000000000000000001300327754000157415ustar00rootroot00000000000000nemu-0.3.1/docs/debconf-talk/Makefile000066400000000000000000000010521300327754000173770ustar00rootroot00000000000000IMGS = openlogo.svg PDF_IMGS := $(patsubst %.svg,%.pdf,$(patsubst %.dia,%.pdf,$(IMGS))) DVI_IMGS := $(patsubst %.svg,%.eps,$(patsubst %.dia,%.eps,$(IMGS))) ALL = nemu.pdf all: $(ALL) %.eps: %.dia inkscape -E $@ $< %.pdf: %.dia inkscape -A $@ $< %.eps: %.svg inkscape -E $@ $< %.pdf: %.svg inkscape -A $@ $< %.ps: %.dvi dvips $< nemu.dvi: nemu.tex $(DVI_IMGS) latex $< latex $< nemu.pdf: nemu.tex $(PDF_IMGS) pdflatex $< pdflatex $< clean: rm -f $(PDF_IMGS) $(DVI_IMGS) *.aux *.out *.log *.dvi *.nav *.snm \ *.toc *.vrb *.bak $(ALL) nemu-0.3.1/docs/debconf-talk/nemu.tex000066400000000000000000000053051300327754000174320ustar00rootroot00000000000000% vim:ts=2:sw=2:et:ai:sts=2 \documentclass{beamer} \mode { \usetheme{Boadilla} % simple \usecolortheme{seahorse} \useinnertheme{rectangles} } \usepackage[english]{babel} \usepackage[utf8]{inputenc} \usepackage[normalem]{ulem} \DeclareRobustCommand{\hsout}[1]{\texorpdfstring{\sout{#1}}{#1}} \pgfdeclareimage[height=0.5cm]{debian-logo}{openlogo} \pgfdeclareimage[height=2cm]{debian-logo-big}{openlogo} \title{Introducing Nemu} \subtitle{Network EMUlator in a \hsout{box} Python library} \author{Martín Ferrari} \institute[DebConf 12]{\pgfuseimage{debian-logo-big}} \date{July 14, 2012} \subject{Talks} \logo{\pgfuseimage{debian-logo}} \begin{document} \begin{frame} \titlepage \end{frame} \begin{frame}{What is Nemu?} \begin{itemize} \item A \alert{python} library, \item to create \alert{emulated networks}, \item and run \alert{tests and experiments} \item that can be \alert{repeated}. \item[]{} \item[] \em{A by-product of research that found a practical use.} \end{itemize} \end{frame} \begin{frame}{What can I use it for?} \begin{itemize} \item Test your new peer-to-peer application. \item[] \small{\em{Run 50 instances in your machine!}} \vfill \item Observe behaviour on unreliable networks. \item[] \small{\em{Configure packet loss, delay, throughput...}} \vfill \item Justify your changes with experimental data. \item[] \small{\em{Make your script output nice GNUPlot graphs!}} \vfill \item Verify configuration changes before applying to the production network. \item[] \small{\em{Change iptables and routing configuration with confidence!}} \vfill \item Distribute your experiment/test easily, no configuration needed! \item[] \small{\em{Here, execute this and see for yourself!}} \end{itemize} \end{frame} \begin{frame}[fragile]{How does it look like?} \begin{semiverbatim} import nemu node0 = nemu.Node() node1 = nemu.Node() (if0, if1) = nemu.P2PInterface.create_pair(node0, node1) if0.up = if1.up = True if0.add_v4_address(address='10.0.0.1', prefix_len=24) if1.add_v4_address(address='10.0.0.2', prefix_len=24) node0.system("ping -c 5 10.0.0.2") \end{semiverbatim} \end{frame} \begin{frame}{Resources} Related projects:\\ \begin{itemize} \item NEPI: original project that spawned the development of Nemu.\\ High-level network description, GUI, multiple back-ends. \item Mininet: similar project from Stanford, developed at the same time. \end{itemize} \hfill Links:\\ \begin{itemize} \item Nemu homepage: \texttt{http://code.google.com/p/nemu/} \item NEPI homepage: \texttt{http://nepi.pl.sophia.inria.fr/} \item This slides + code: \texttt{\$HOME/source/browse/docs/debconf-talk/} \end{itemize} \end{frame} \end{document} nemu-0.3.1/docs/debconf-talk/openlogo.svg000066400000000000000000000246061300327754000203140ustar00rootroot00000000000000 ]> nemu-0.3.1/docs/debconf-talk/test-network.py000077500000000000000000000050001300327754000207570ustar00rootroot00000000000000#!/usr/bin/env python # vim:ts=4:sw=4:et:ai:sts=4 import os, nemu, subprocess, time xterm = nemu.environ.find_bin("xterm") mtr = nemu.environ.find_bin("mtr") X = "DISPLAY" in os.environ and xterm # Do not use X stuff. X = False # each Node is a netns node = [] switch = [] iface = {} SIZE = 5 for i in range(SIZE): node.append(nemu.Node(forward_X11 = X)) next_pair = (i, i + 1) prev_pair = (i, i - 1) if i < SIZE - 1: iface[(i, i + 1)] = nemu.NodeInterface(node[i]) iface[(i, i + 1)].up = True iface[(i, i + 1)].add_v4_address(address='10.0.%d.1' % i, prefix_len=24) if i > 0: iface[(i, i - 1)] = nemu.NodeInterface(node[i]) iface[(i, i - 1)].up = True iface[(i, i - 1)].add_v4_address(address='10.0.%d.2' % (i - 1), prefix_len=24) switch.append(nemu.Switch()) switch[-1].connect(iface[(i, i - 1)]) switch[-1].connect(iface[(i - 1, i)]) switch[-1].up = True # Configure routing for j in range(SIZE - 1): if j in (i, i - 1): continue if j < i: node[i].add_route(prefix='10.0.%d.0' % j, prefix_len=24, nexthop='10.0.%d.1' % (i - 1)) else: node[i].add_route(prefix='10.0.%d.0' % j, prefix_len=24, nexthop='10.0.%d.2' % i) print "Nodes started with pids: %s" % str([n.pid for n in node]) #switch0 = nemu.Switch( # bandwidth = 100 * 1024 * 1024, # delay = 0.1, # 100 ms # delay_jitter = 0.01, # 10ms # delay_correlation = 0.25, # 25% correlation # loss = 0.005) # Test connectivity first. Run process, hide output and check # return code null = file("/dev/null", "w") app0 = node[0].Popen("ping -c 1 10.0.%d.2" % (SIZE - 2), shell=True, stdout=null) ret = app0.wait() assert ret == 0 app1 = node[-1].Popen("ping -c 1 10.0.0.1", shell = True, stdout = null) ret = app1.wait() assert ret == 0 print "Connectivity IPv4 OK!" if X: app = [] for i in range(SIZE - 1): height = 102 base = 25 cmd = "%s -eni %s" % (nemu.environ.TCPDUMP_PATH, iface[(i, i + 1)].name) xtermcmd = "%s -geometry 100x5+0+%d -T %s -e %s" % ( xterm, i * height + base, "node%d" % i, cmd) app.append(node[i].Popen(xtermcmd, shell=True)) app.append(node[-1].Popen("%s -n 10.0.0.1" % mtr, shell=True)) app[-1].wait() for i in range(SIZE - 1): app[i].signal() app[i].wait() else: node[-1].system("%s -n --report 10.0.0.1" % mtr) nemu-0.3.1/docs/protocol.txt000066400000000000000000000061051300327754000160140ustar00rootroot00000000000000Protocol format: ---------------- RFC 2821-like. At start-up, server sends 220 code and greeting text To close the connection and the node, client sends QUIT command, and server replies with 221 code. Command Subcmd Arguments Response Effect QUIT 221 Close the netns IF LIST [if#] 200 serialised data ip link list IF SET if# k v k v... 200/500 ip link set (1) IF RTRN if# ns 200/500 ip link set netns $ns IF DEL if# 200/500 ip link del ADDR LIST [if#] 200 serialised data ip addr list ADDR ADD if# addr_spec 200/500 ip addr add ADDR DEL if# addr_spec 200/500 ip addr del ROUT LIST 200 serialised data ip route list ROUT ADD route_spec 200/500 ip route add ROUT DEL route_spec 200/500 ip route del PROC CRTE argv0 argv1... 200/500 (2) PROC USER username 200/500 (3) PROC CWD cwd 200/500 (3) PROC ENV k v k v... 200/500 (3) PROC SIN 354+200/500 (4) PROC SOUT 354+200/500 (4) PROC SERR 354+200/500 (4) PROC RUN 200 /500 (5) PROC ABRT 200 (5) PROC POLL 200 /450/500 check if process alive PROC WAIT 200 /500 waitpid(pid) PROC KILL 200/500 kill(pid, signal) X11 354+200/500 (6) (1) valid arguments: mtu , up <0|1>, name , lladdr , broadcast , multicast <0|1>, arp <0|1>. (2) After PROC CRTE, only secondary PROC cmds are accepted until finished. The parameters are parsed as base64-encoded strings if they start with a '=' character. (3) Secondary PROC commands, only valid after PROC CRTE. All parameters parsed as base64-encoded strings. Arguments for PROC ENV are pairs of key-value to set up the process environment. (4) Secondary PROC commands, only valid after PROC CRTE. Server reply 354 and waits for a file descriptor to be passed along with a duplicate of the same command. Answers 200/500 after processing the file descriptor. (5) Secondary PROC commands, unconditionally end the PROC transaction. If RUN was successful, the process is started and the process ID is returned as the first token of the reply. (6) Enable X11 forwarding, using the specified protocol and data for authentication. A opened socket ready to receive X connections is passed over the channel. Answers 200/500 after transmitting the file descriptor. Sample session -------------- Parent calls socketpair(), fork() and unshare(); thus creating a new netns; protocol exchanges occur through the socket. 220 Hello IF LIST 200-[{id: 1, mtu: 16436, name: lo, up: true}, {id: 10, 200 lladdr: '12:34:56:78:9a:bc', mtu: 1500, name: eth0, up: true}] IF SET 10 MTU 1492 200 Ok. ADDR ADD 10 10.0.0.1 24 10.0.0.255 200 Ok. ADDR DEL 10 192.168.1.1 24 500 Address does not exist. PROC CRTE /bin/sh sh -c sleep 10 200 Entering PROC mode. PROC USER nobody 200 Program will run as `nobody'. PROC CWD / 200 CWD set to /. PROC SIN 354 Waiting for FD. Server calls recvmsg() Client calls sendmsg() 200 FD received OK. PROC RUN 200 1649 pid process started. PROC WAIT 1649 Time passes... 200 0 exit code QUIT 221 Exiting... nemu-0.3.1/docs/sample-api.txt000066400000000000000000000075361300327754000162140ustar00rootroot00000000000000#/usr/bin/env python # vim:ts=4:sw=4:et:ai:sts=4 import nemu import signal # run_as: user to setuid() to before running applications (this is assumed to # run as root) nemu.config.run_as = 'nobody' # Clean-up is essential to avoid leaving bridge devices all over the place # (luckily, the veths die automatically). This installs signals and exit # handlers. nemu.set_cleanup_hooks(on_exit = True, on_signals = [signal.SIGTERM, signal.SIGINT]) # each Node is a netns a = nemu.Node() b = nemu.Node() print "Nodes started with pids: %d and %d" % (a.pid, b.pid) # interface object maps to a veth pair with one end in a netns if0 = a.add_if(lladdr = '42:71:e0:90:ca:42') # This is equivalent #if0 = nemu.NodeInterface(a) #if0.lladdr = '42:71:e0:90:ca:42' if1 = b.add_if(mtu = 1492) # for using with a tun device, to connect to the outside world if2 = b.import_if('tun0') # each Switch is a linux bridge, all the parameters are applied to the # associated interfaces as tc qdiscs. switch0 = nemu.Switch(bandwidth = 100 * 1024 * 1024, delay = 0.01, delay_jitter = 0.001, delay_correlation = 0.25, delay_distribution = 'normal', loss = 0.005, loss_correlation = 0.20, dup = 0.005, dup_correlation = 0.25, corrupt = 0.005, corrupt_correlation = 0.25) # connect to the bridge switch0.connect(if0) switch0.connect(if1) # Should be experimented with Tom Geoff's patch to see if the bridge could be # avoided; but for that the API would be slightly different, as these would be # point-to-point interfaces and links. # ppp0 = nemu.PPPSwitch(a, b, bandwidth = ....) # if0 = ppp0.interface(a) # For now, we have simple P2P interfaces: (pppa, pppb) = nemu.P2PInterface.create_pair(a, b) # Add and connect a tap device (as if a external router were plugged into a # switch) if2 = nemu.ImportedInterface('tap0') switch0.connect(if2) switch0.up = True if0.up = True if1.up = True # addresses as iproute if0.add_v4_address(addr = '10.0.0.1', prefix_len = 24) if0.add_v6_address(addr = 'fe80::222:19ff:fe22:615d', prefix_len = 64) if1.add_v4_address(addr = '10.0.0.2', prefix_len = 24, broadcast = '10.1.0.255') # ditto #a.add_route(prefix = '0', prefix_len = 0, nexthop = '10.0.0.2') a.add_default_route(nexthop = '10.0.0.2') b.add_route(prefix = '10.1.0.0', prefix_len = 16, nexthop = '10.0.0.1') b.add_route(prefix = '11.1.0.1', prefix_len = 32, device = if1) # Some inspection methods: they will not read internal data but query the # kernel addrs = if0.get_addresses() stats = if0.get_stats() routes = a.get_routes() ifaces = a.get_interfaces() nodes = nemu.get_nodes() switches = nemu.get_switches() stats = link0.get_stats() # Run a process in background import subprocess app0 = a.Popen("ping -c 3 10.0.0.2", shell = True, stdout = subprocess.PIPE) print app0.stdout.readline() app0.wait() # Run, capture output and wait() stdout = a.backticks(["ping", "-c", "3", "10.0.0.2"]) # Run an process with a pseudo-tty associated to it; provide a UNIX socket to # interact with the process app2 = a.start_tty_process("/bin/bash") # app2.sockname, app2.sockfd app2.wait() # Example to set up a linear topology def setup_linear_topology(n, bd, delay): nodes = [] for i in range(n): nodes.append(nemu.Node()) for i in range(n - 1): if1 = nodes[i].add_if() if2 = nodes[i + 1].add_if() if1.add_v4_address(addr = ('10.0.%d.2' % i), prefix_len = 24) if2.add_v4_address(addr = ('10.0.%d.1' % i), prefix_len = 24) switch = nemu.Switch(bandwidth = bd, delay = delay) switch.connect(if1) switch.connect(if2) for i in range(n): for j in range(n): if abs(i - j) <= 1: continue nodes[i].add_route(prefix = ('10.0.%d.0' % j), prefix_len = 24, nexthop = ('10.0.%d.%d' % ((i, 1) if i < j else (i - 1, 2))) ) return nodes nemu-0.3.1/examples/000077500000000000000000000000001300327754000142765ustar00rootroot00000000000000nemu-0.3.1/examples/sample.py000077500000000000000000000046431300327754000161430ustar00rootroot00000000000000#!/usr/bin/env python # vim:ts=4:sw=4:et:ai:sts=4 import os, nemu, subprocess, time # Uncomment for verbose operation. #nemu.environ.set_log_level(nemu.environ.LOG_DEBUG) xterm = nemu.environ.find_bin("xterm") X = "DISPLAY" in os.environ and xterm # each Node is a netns node0 = nemu.Node(forward_X11 = X) node1 = nemu.Node(forward_X11 = X) node2 = nemu.Node(forward_X11 = X) print "Nodes started with pids: %s" % str((node0.pid, node1.pid, node2.pid)) # interface object maps to a veth pair with one end in a netns if0 = nemu.NodeInterface(node0) if1a = nemu.NodeInterface(node1) # Between node1 and node2, we use a P2P interface (if1b, if2) = nemu.P2PInterface.create_pair(node1, node2) switch0 = nemu.Switch( bandwidth = 100 * 1024 * 1024, delay = 0.1, # 100 ms delay_jitter = 0.01, # 10ms delay_correlation = 0.25, # 25% correlation loss = 0.005) # connect the interfaces switch0.connect(if0) switch0.connect(if1a) # bring the interfaces up switch0.up = if0.up = if1a.up = if1b.up = if2.up = True # Add IP addresses if0.add_v4_address(address = '10.0.0.1', prefix_len = 24) if1a.add_v4_address(address = '10.0.0.2', prefix_len = 24) if1b.add_v4_address(address = '10.0.1.1', prefix_len = 24) if2.add_v4_address(address = '10.0.1.2', prefix_len = 24) # Configure routing node0.add_route(prefix = '10.0.1.0', prefix_len = 24, nexthop = '10.0.0.2') node2.add_route(prefix = '10.0.0.0', prefix_len = 24, nexthop = '10.0.1.1') # Test connectivity first. Run process, hide output and check # return code null = file("/dev/null", "w") app0 = node0.Popen("ping -c 1 10.0.1.2", shell = True, stdout = null) ret = app0.wait() assert ret == 0 app1 = node2.Popen("ping -c 1 10.0.0.1", shell = True, stdout = null) ret = app1.wait() assert ret == 0 print "Connectivity IPv4 OK!" # Some nice visual demo. if X: print "Running ping and tcpdump in different nodes." app1 = node1.Popen("%s -geometry -0+0 -e %s -ni %s" % (xterm, nemu.environ.TCPDUMP_PATH, if1b.name), shell = True) time.sleep(3) app0 = node0.Popen("%s -geometry +0+0 -e ping -c 10 10.0.1.2" % xterm, shell = True) app0.wait() app1.signal() app1.wait() print "Running network conditions test." # When using a args list, the shell is not needed app2 = node0.Popen(["ping", "-q", "-c1000", "-f", "10.0.1.2"], stdout = subprocess.PIPE) out, err = app2.communicate() print "Ping outout:" print out nemu-0.3.1/setup.cfg000066400000000000000000000000201300327754000142710ustar00rootroot00000000000000[clean] all = 1 nemu-0.3.1/setup.py000077500000000000000000000012021300327754000141700ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- # vim: ts=4:sw=4:et:ai:sts=4 from distutils.core import setup, Extension, Command setup( name = 'nemu', version = '0.3.1', description = 'A lightweight network emulator embedded in a small ' 'python library.', author = 'Martín Ferrari, Alina Quereilhac', author_email = 'martin.ferrari@gmail.com, aquereilhac@gmail.com', url = 'http://code.google.com/p/nemu/', license = 'GPLv2', platforms = 'Linux', packages = ['nemu'], package_dir = {'': 'src'} ) nemu-0.3.1/src/000077500000000000000000000000001300327754000132475ustar00rootroot00000000000000nemu-0.3.1/src/nemu/000077500000000000000000000000001300327754000142135ustar00rootroot00000000000000nemu-0.3.1/src/nemu/__init__.py000066400000000000000000000043771300327754000163370ustar00rootroot00000000000000# vim:ts=4:sw=4:et:ai:sts=4 # -*- coding: utf-8 -*- # Copyright 2010, 2011 INRIA # Copyright 2011 Martín Ferrari # # This file is part of Nemu. # # Nemu is free software: you can redistribute it and/or modify it under the # terms of the GNU General Public License version 2, as published by the Free # Software Foundation. # # Nemu is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR # A PARTICULAR PURPOSE. See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along with # Nemu. If not, see . """Nemu package. Nemu (Netwok EMUlator) is a small Python library to create emulated networks and run and test programs in them. """ # pylint: disable=W0401,R0903 import os, pwd from nemu.node import * from nemu.interface import * class _Config(object): """Global configuration singleton for Nemu.""" def __init__(self): self._run_as = 65534 try: pwd.getpwnam('nobody') self._run_as = 'nobody' except KeyError: pass # User not found. def _set_run_as(self, user): """Setter for `run_as'.""" if str(user).isdigit(): uid = int(user) try: _user = pwd.getpwuid(uid)[0] except: raise AttributeError("UID %d does not exist" % int(user)) run_as = int(user) else: try: uid = pwd.getpwnam(str(user))[2] except: raise AttributeError("User %s does not exist" % str(user)) run_as = str(user) if uid == 0: raise AttributeError("Cannot run as root by default") self._run_as = run_as return run_as def _get_run_as(self): """Setter for `run_as'.""" return self._run_as run_as = property(_get_run_as, _set_run_as, None, "Default user to run applications as") config = _Config() # pylint: disable=C0103 # FIXME: set atfork hooks # http://code.google.com/p/python-atfork/source/browse/atfork/__init__.py #def set_cleanup_hooks(on_exit = False, on_signals = []): # pass nemu-0.3.1/src/nemu/environ.py000066400000000000000000000152341300327754000162520ustar00rootroot00000000000000# vim:ts=4:sw=4:et:ai:sts=4 # -*- coding: utf-8 -*- # Copyright 2010, 2011 INRIA # Copyright 2011 Martín Ferrari # # This file is part of Nemu. # # Nemu is free software: you can redistribute it and/or modify it under the # terms of the GNU General Public License version 2, as published by the Free # Software Foundation. # # Nemu is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR # A PARTICULAR PURPOSE. See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along with # Nemu. If not, see . import errno, os, os.path, socket, subprocess, sys, syslog from syslog import LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG __all__ = ["IP_PATH", "TC_PATH", "BRCTL_PATH", "SYSCTL_PATH", "HZ"] __all__ += ["TCPDUMP_PATH", "NETPERF_PATH", "XAUTH_PATH", "XDPYINFO_PATH"] __all__ += ["execute", "backticks", "eintr_wrapper"] __all__ += ["find_listen_port"] __all__ += ["LOG_ERR", "LOG_WARNING", "LOG_NOTICE", "LOG_INFO", "LOG_DEBUG"] __all__ += ["set_log_level", "logger"] __all__ += ["error", "warning", "notice", "info", "debug"] def find_bin(name, extra_path = None): """Try hard to find the location of needed programs.""" search = [] if "PATH" in os.environ: search += os.environ["PATH"].split(":") search.extend(os.path.join(x, y) for x in ("/", "/usr/", "/usr/local/") for y in ("bin", "sbin")) if extra_path: search += extra_path for dirr in search: path = os.path.join(dirr, name) if os.path.exists(path): return path return None def find_bin_or_die(name, extra_path = None): """Try hard to find the location of needed programs; raise on failure.""" res = find_bin(name, extra_path) if not res: raise RuntimeError("Cannot find `%s', impossible to continue." % name) return res IP_PATH = find_bin_or_die("ip") TC_PATH = find_bin_or_die("tc") BRCTL_PATH = find_bin_or_die("brctl") SYSCTL_PATH = find_bin_or_die("sysctl") # Optional tools TCPDUMP_PATH = find_bin("tcpdump") NETPERF_PATH = find_bin("netperf") XAUTH_PATH = find_bin("xauth") XDPYINFO_PATH = find_bin("xdpyinfo") # Seems this is completely bogus. At least, we can assume that the internal HZ # is bigger than this. HZ = os.sysconf("SC_CLK_TCK") try: os.stat("/sys/class/net") except: raise RuntimeError("Sysfs does not seem to be mounted, impossible to " + "continue.") def execute(cmd): """Execute a command, if the return value is non-zero, raise an exception. Raises: RuntimeError: the command was unsuccessful (return code != 0). """ debug("execute(%s)" % cmd) null = open("/dev/null", "r+") proc = subprocess.Popen(cmd, stdout = null, stderr = subprocess.PIPE) _, err = proc.communicate() if proc.returncode != 0: raise RuntimeError("Error executing `%s': %s" % (" ".join(cmd), err)) def backticks(cmd): """Execute a command and capture its output. If the return value is non-zero, raise an exception. Returns: (stdout, stderr): tuple containing the captured output. Raises: RuntimeError: the command was unsuccessful (return code != 0). """ debug("backticks(%s)" % cmd) proc = subprocess.Popen(cmd, stdout = subprocess.PIPE, stderr = subprocess.PIPE) out, err = proc.communicate() if proc.returncode != 0: raise RuntimeError("Error executing `%s': %s" % (" ".join(cmd), err)) return out def eintr_wrapper(func, *args): "Wraps some callable with a loop that retries on EINTR." while True: try: return func(*args) except OSError, ex: # pragma: no cover if ex.errno == errno.EINTR: continue raise except IOError, ex: # pragma: no cover if ex.errno == errno.EINTR: continue raise def find_listen_port(family = socket.AF_INET, type = socket.SOCK_STREAM, proto = 0, addr = "127.0.0.1", min_port = 1, max_port = 65535): sock = socket.socket(family, type, proto) for port in range(min_port, max_port + 1): try: sock.bind((addr, port)) return sock, port except socket.error: pass raise RuntimeError("Cannot find an usable port in the range specified") # Logging _log_level = LOG_WARNING _log_use_syslog = False _log_stream = sys.stderr _log_syslog_opts = () _log_pid = os.getpid() def set_log_level(level): "Sets the log level for console messages, does not affect syslog logging." global _log_level assert level > LOG_ERR and level <= LOG_DEBUG _log_level = level def set_log_output(stream): "Redirect console messages to the provided stream." global _log_stream assert hasattr(stream, "write") and hasattr(stream, "flush") _log_stream = stream def log_use_syslog(use = True, ident = None, logopt = 0, facility = syslog.LOG_USER): "Enable or disable the use of syslog for logging messages." global _log_use_syslog, _log_syslog_opts _log_syslog_opts = (ident, logopt, facility) _log_use_syslog = use _init_log() def _init_log(): if not _log_use_syslog: syslog.closelog() return (ident, logopt, facility) = _log_syslog_opts if not ident: #ident = os.path.basename(sys.argv[0]) ident = "nemu" syslog.openlog("%s[%d]" % (ident, os.getpid()), logopt, facility) info("Syslog logging started") def logger(priority, message): "Print a log message in syslog, console or both." if _log_use_syslog: if os.getpid() != _log_pid: _init_log() # Need to tell syslog the new PID. syslog.syslog(priority, message) return if priority > _log_level: return eintr_wrapper(_log_stream.write, "[%d] %s\n" % (os.getpid(), message.rstrip())) _log_stream.flush() def error(message): logger(LOG_ERR, message) def warning(message): logger(LOG_WARNING, message) def notice(message): logger(LOG_NOTICE, message) def info(message): logger(LOG_INFO, message) def debug(message): logger(LOG_DEBUG, message) def _custom_hook(tipe, value, traceback): # pragma: no cover """Custom exception hook, to print nested exceptions information.""" if hasattr(value, "child_traceback"): sys.stderr.write("Nested exception, original traceback " + "(most recent call last):\n") sys.stderr.write(value.child_traceback + ("-" * 70) + "\n") sys.__excepthook__(tipe, value, traceback) sys.excepthook = _custom_hook nemu-0.3.1/src/nemu/interface.py000066400000000000000000000451561300327754000165400ustar00rootroot00000000000000# vim:ts=4:sw=4:et:ai:sts=4 # -*- coding: utf-8 -*- # Copyright 2010, 2011 INRIA # Copyright 2011 Martín Ferrari # # This file is part of Nemu. # # Nemu is free software: you can redistribute it and/or modify it under the # terms of the GNU General Public License version 2, as published by the Free # Software Foundation. # # Nemu is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR # A PARTICULAR PURPOSE. See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along with # Nemu. If not, see . import os, weakref import nemu.iproute from nemu.environ import * __all__ = ['NodeInterface', 'P2PInterface', 'ImportedInterface', 'ImportedNodeInterface', 'Switch'] class Interface(object): """Just a base class for the *Interface classes: assign names and handle destruction.""" _nextid = 0 @staticmethod def _gen_next_id(): n = Interface._nextid Interface._nextid += 1 return n @staticmethod def _gen_if_name(): n = Interface._gen_next_id() # Max 15 chars return "NETNSif-%.4x%.3x" % (os.getpid(), n) def __init__(self, index): self._idx = index debug("%s(0x%x).__init__(), index = %d" % (self.__class__.__name__, id(self), index)) def __del__(self): debug("%s(0x%x).__del__()" % (self.__class__.__name__, id(self))) self.destroy() def destroy(self): raise NotImplementedError @property def index(self): """Interface index as seen by the kernel.""" return self._idx @property def control(self): """Associated interface in the main name space (if it exists). Only control interfaces can be put into a Switch, for example.""" return None class NSInterface(Interface): """Add user-facing methods for interfaces that go into a netns.""" def __init__(self, node, index): super(NSInterface, self).__init__(index) self._slave = node._slave # Disable auto-configuration # you wish: need to take into account the nonetns mode; plus not # touching some pre-existing ifaces #node.system([SYSCTL_PATH, '-w', 'net.ipv6.conf.%s.autoconf=0' % #self.name]) node._add_interface(self) # some black magic to automatically get/set interface attributes def __getattr__(self, name): # If name starts with _, it must be a normal attr if name[0] == '_': return super(Interface, self).__getattribute__(name) try: slave = super(Interface, self).__getattribute__("_slave") except: # Not initialised yet return super(Interface, self).__getattribute__(name) iface = slave.get_if_data(self.index) return getattr(iface, name) def __setattr__(self, name, value): if name[0] == '_': # forbid anything that doesn't start with a _ super(Interface, self).__setattr__(name, value) return iface = nemu.iproute.interface(index = self.index) setattr(iface, name, value) return self._slave.set_if(iface) def add_v4_address(self, address, prefix_len, broadcast = None): addr = nemu.iproute.ipv4address(address, prefix_len, broadcast) self._slave.add_addr(self.index, addr) def add_v6_address(self, address, prefix_len): addr = nemu.iproute.ipv6address(address, prefix_len) self._slave.add_addr(self.index, addr) def del_v4_address(self, address, prefix_len, broadcast = None): addr = nemu.iproute.ipv4address(address, prefix_len, broadcast) self._slave.del_addr(self.index, addr) def del_v6_address(self, address, prefix_len): addr = nemu.iproute.ipv6address(address, prefix_len) self._slave.del_addr(self.index, addr) def get_addresses(self): addresses = self._slave.get_addr_data(self.index) ret = [] for a in addresses: if hasattr(a, 'broadcast'): ret.append(dict( address = a.address, prefix_len = a.prefix_len, broadcast = a.broadcast, family = 'inet')) else: ret.append(dict( address = a.address, prefix_len = a.prefix_len, family = 'inet6')) return ret class NodeInterface(NSInterface): """Class to create and handle a virtual interface inside a name space, it can be connected to a Switch object with emulation of link characteristics.""" def __init__(self, node): """Create a new interface. `node' is the name space in which this interface should be put.""" self._slave = None if1 = nemu.iproute.interface(name = self._gen_if_name()) if2 = nemu.iproute.interface(name = self._gen_if_name()) ctl, ns = nemu.iproute.create_if_pair(if1, if2) try: nemu.iproute.change_netns(ns, node.pid) except: nemu.iproute.del_if(ctl) # the other interface should go away automatically raise self._control = SlaveInterface(ctl.index) super(NodeInterface, self).__init__(node, ns.index) @property def control(self): return self._control def destroy(self): if not self._slave: return debug("NodeInterface(0x%x).destroy()" % id(self)) if self.index in self._slave.get_if_data(): self._slave.del_if(self.index) self._slave = None class P2PInterface(NSInterface): """Class to create and handle point-to-point interfaces between name spaces, without using Switch objects. Those do not allow any kind of traffic shaping. As two interfaces need to be created, instead of using the class constructor, use the P2PInterface.create_pair() static method.""" @staticmethod def create_pair(node1, node2): """Create and return a pair of connected P2PInterface objects, assigned to name spaces represented by `node1' and `node2'.""" if1 = nemu.iproute.interface(name = P2PInterface._gen_if_name()) if2 = nemu.iproute.interface(name = P2PInterface._gen_if_name()) pair = nemu.iproute.create_if_pair(if1, if2) try: nemu.iproute.change_netns(pair[0], node1.pid) nemu.iproute.change_netns(pair[1], node2.pid) except: nemu.iproute.del_if(pair[0]) # the other interface should go away automatically raise o1 = P2PInterface.__new__(P2PInterface) super(P2PInterface, o1).__init__(node1, pair[0].index) o2 = P2PInterface.__new__(P2PInterface) super(P2PInterface, o2).__init__(node2, pair[1].index) return o1, o2 def __init__(self): "Not to be called directly. Use P2PInterface.create_pair()" raise RuntimeError(P2PInterface.__init__.__doc__) def destroy(self): if not self._slave: return debug("P2PInterface(0x%x).destroy()" % id(self)) if self.index in self._slave.get_if_data(): self._slave.del_if(self.index) self._slave = None class ImportedNodeInterface(NSInterface): """Class to handle already existing interfaces inside a name space: real devices, tun devices, etc. The flag 'migrate' in the constructor indicates that the interface needs to be moved inside the name space. On destruction, the interface will be restored to the original name space and will try to restore the original state.""" def __init__(self, node, iface, migrate = True): self._slave = None self._migrate = migrate if self._migrate: iface = nemu.iproute.get_if(iface) self._original_state = iface.copy() # Change the name to avoid clashes iface.name = self._gen_if_name() nemu.iproute.set_if(iface) # Migrate it nemu.iproute.change_netns(iface, node.pid) else: iface = node._slave.get_if_data(iface) self._original_state = iface.copy() super(ImportedNodeInterface, self).__init__(node, iface.index) def destroy(self): # override: restore as much as possible if not self._slave: return debug("ImportedNodeInterface(0x%x).destroy()" % id(self)) if self.index in self._slave.get_if_data(): if self._migrate: self._slave.change_netns(self.index, os.getpid()) else: self._slave.set_if(self._original_state) if self._migrate: # else, assume it is already in the main name space nemu.iproute.set_if(self._original_state) self._slave = None class TapNodeInterface(NSInterface): """Class to create a tap interface inside a name space, it can be connected to a Switch object with emulation of link characteristics.""" def __init__(self, node, use_pi = False): """Create a new tap interface. 'node' is the name space in which this interface should be put.""" self._fd = None self._slave = None iface = nemu.iproute.interface(name = self._gen_if_name()) iface, self._fd = nemu.iproute.create_tap(iface, use_pi = use_pi) nemu.iproute.change_netns(iface.name, node.pid) super(TapNodeInterface, self).__init__(node, iface.index) @property def fd(self): return self._fd def destroy(self): if not self._fd: return debug("TapNodeInterface(0x%x).destroy()" % id(self)) try: os.close(self._fd) except: pass class TunNodeInterface(NSInterface): """Class to create a tun interface inside a name space, it can be connected to a Switch object with emulation of link characteristics.""" def __init__(self, node, use_pi = False): """Create a new tap interface. 'node' is the name space in which this interface should be put.""" self._fd = None self._slave = None iface = nemu.iproute.interface(name = self._gen_if_name()) iface, self._fd = nemu.iproute.create_tap(iface, use_pi = use_pi, tun = True) nemu.iproute.change_netns(iface.name, node.pid) super(TunNodeInterface, self).__init__(node, iface.index) @property def fd(self): return self._fd def destroy(self): if not self._fd: return debug("TunNodeInterface(0x%x).destroy()" % id(self)) try: os.close(self._fd) except: pass class ExternalInterface(Interface): """Add user-facing methods for interfaces that run in the main namespace.""" @property def control(self): # This is *the* control interface return self # some black magic to automatically get/set interface attributes def __getattr__(self, name): iface = nemu.iproute.get_if(self.index) return getattr(iface, name) def __setattr__(self, name, value): if name[0] == '_': # forbid anything that doesn't start with a _ super(ExternalInterface, self).__setattr__(name, value) return iface = nemu.iproute.interface(index = self.index) setattr(iface, name, value) return nemu.iproute.set_if(iface) def add_v4_address(self, address, prefix_len, broadcast = None): addr = nemu.iproute.ipv4address(address, prefix_len, broadcast) nemu.iproute.add_addr(self.index, addr) def add_v6_address(self, address, prefix_len): addr = nemu.iproute.ipv6address(address, prefix_len) nemu.iproute.add_addr(self.index, addr) def del_v4_address(self, address, prefix_len, broadcast = None): addr = nemu.iproute.ipv4address(address, prefix_len, broadcast) nemu.iproute.del_addr(self.index, addr) def del_v6_address(self, address, prefix_len): addr = nemu.iproute.ipv6address(address, prefix_len) nemu.iproute.del_addr(self.index, addr) def get_addresses(self): addresses = nemu.iproute.get_addr_data(self.index) ret = [] for a in addresses: if hasattr(a, 'broadcast'): ret.append(dict( address = a.address, prefix_len = a.prefix_len, broadcast = a.broadcast, family = 'inet')) else: ret.append(dict( address = a.address, prefix_len = a.prefix_len, family = 'inet6')) return ret class SlaveInterface(ExternalInterface): """Class to handle the main-name-space-facing half of NodeInterface. Does nothing, just avoids any destroy code.""" def destroy(self): pass class ImportedInterface(ExternalInterface): """Class to handle already existing interfaces. Analogous to ImportedNodeInterface, this class only differs in that the interface is not migrated inside the name space. This kind of interfaces can only be connected to Switch objects and not assigned to a name space. On destruction, the code will try to restore the interface to the state it was in before being imported into nemu.""" def __init__(self, iface): self._original_state = None iface = nemu.iproute.get_if(iface) self._original_state = iface.copy() super(ImportedInterface, self).__init__(iface.index) # FIXME: register somewhere for destruction! def destroy(self): # override: restore as much as possible if self._original_state: debug("ImportedInterface(0x%x).destroy()" % id(self)) nemu.iproute.set_if(self._original_state) self._original_state = None # Switch is just another interface type class Switch(ExternalInterface): @staticmethod def _gen_br_name(): n = Switch._gen_next_id() # Max 15 chars return "NETNSbr-%.4x%.3x" % (os.getpid(), n) def __init__(self, **args): """Creates a new Switch object, which models a linux bridge device. Parameters are passed to the set_parameters() method after creation.""" # attributes init self._idx = None self._parameters = {} self._ports = weakref.WeakValueDictionary() iface = nemu.iproute.create_bridge(self._gen_br_name()) super(Switch, self).__init__(iface.index) # FIXME: is this correct/desirable/etc? self.stp = False self.forward_delay = 0 # FIXME: register somewhere if args: self.set_parameters(**args) def __getattr__(self, name): iface = nemu.iproute.get_bridge(self.index) return getattr(iface, name) def __setattr__(self, name, value): if name[0] == '_': # forbid anything that doesn't start with a _ super(Switch, self).__setattr__(name, value) return # Set ports if name in ('up', 'mtu'): for i in self._ports.values(): if self._check_port(i.index): setattr(i, name, value) # Set bridge iface = nemu.iproute.bridge(index = self.index) setattr(iface, name, value) nemu.iproute.set_bridge(iface) def destroy(self): if not self.index: return debug("Switch(0x%x).destroy()" % id(self)) # Verify they are still there for p in self._ports.keys(): self._check_port(p) self.up = False for p in self._ports.values(): self.disconnect(p) self._ports.clear() nemu.iproute.del_bridge(self.index) self._idx = None def connect(self, iface): assert iface.control.index not in self._ports try: self._apply_parameters(self._parameters, iface.control) nemu.iproute.add_bridge_port(self.index, iface.control.index) except: self._apply_parameters({}, iface.control) raise iface.control.up = self.up iface.control.mtu = self.mtu self._ports[iface.control.index] = iface.control def _check_port(self, port_index): ports = nemu.iproute.get_bridge_data()[2] if self.index in ports and port_index in ports[self.index]: return True # else warning("Switch(0x%x): Port (index = %d) went away." % (id(self), port_index)) del self._ports[port_index] return False def disconnect(self, iface): assert iface.control.index in self._ports if not self._check_port(iface.control.index): return nemu.iproute.del_bridge_port(self.index, iface.control.index) self._apply_parameters({}, iface.control) del self._ports[iface.control.index] def set_parameters(self, bandwidth = None, delay = None, delay_jitter = None, delay_correlation = None, delay_distribution = None, loss = None, loss_correlation = None, dup = None, dup_correlation = None, corrupt = None, corrupt_correlation = None): """Set the parameters that control the link characteristics. For the description of each, refer to netem documentation: http://www.linuxfoundation.org/collaborate/workgroups/networking/netem Arguments: - `bandwidth' should be specified in bits per second. - `delay' and `delay_jitter' are specified in seconds. - `delay_distribution' is the name of a distribution description file; `iproute' comes by default with `normal', `pareto', and `paretonormal'. - `delay_correlation', `loss', `loss_correlation', `dup', `dup_correlation', `corrupt', and `corrupt_correlation' take a percentage value in the form of a number between 0 and 1. (50% is passed as 0.5).""" parameters = dict(bandwidth = bandwidth, delay = delay, delay_jitter = delay_jitter, delay_correlation = delay_correlation, delay_distribution = delay_distribution, loss = loss, loss_correlation = loss_correlation, dup = dup, dup_correlation = dup_correlation, corrupt = corrupt, corrupt_correlation = corrupt_correlation) try: self._apply_parameters(parameters) except: self._apply_parameters(self._parameters) raise self._parameters = parameters def _apply_parameters(self, parameters, port = None): for i in [port] if port else self._ports.values(): nemu.iproute.set_tc(i.index, **parameters) nemu-0.3.1/src/nemu/iproute.py000066400000000000000000001013461300327754000162610ustar00rootroot00000000000000# vim:ts=4:sw=4:et:ai:sts=4 # -*- coding: utf-8 -*- # Copyright 2010, 2011 INRIA # Copyright 2011 Martín Ferrari # # This file is part of Nemu. # # Nemu is free software: you can redistribute it and/or modify it under the # terms of the GNU General Public License version 2, as published by the Free # Software Foundation. # # Nemu is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR # A PARTICULAR PURPOSE. See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along with # Nemu. If not, see . import copy, fcntl, os, re, socket, struct, subprocess, sys from nemu.environ import * # helpers def _any_to_bool(any): if isinstance(any, bool): return any if isinstance(any, int): return any != 0 if isinstance(any, str): if any.isdigit(): return int(any) != 0 if any.lower() == "true": return True if any.lower() == "false": return False return any != "" return bool(any) def _positive(val): v = int(val) if v <= 0: raise ValueError("Invalid value: %d" % v) return v def _non_empty_str(val): if val == "": return None else: return str(val) def _fix_lladdr(addr): foo = addr.lower() if ":" in addr: # Verify sanity and split m = re.search("^" + ":".join(["([0-9a-f]{1,2})"] * 6) + "$", foo) if m is None: raise ValueError("Invalid address: `%s'." % addr) # Fill missing zeros and glue again return ":".join(("0" * (2 - len(x)) + x for x in m.groups())) # Fill missing zeros foo = "0" * (12 - len(foo)) + foo # Verify sanity and split m = re.search("^" + "([0-9a-f]{2})" * 6 + "$", foo) if m is None: raise ValueError("Invalid address: `%s'." % addr) # Glue return ":".join(m.groups()) def _make_getter(attr, conv = lambda x: x): def getter(self): return conv(getattr(self, attr)) return getter def _make_setter(attr, conv = lambda x: x): def setter(self, value): if value == None: setattr(self, attr, None) else: setattr(self, attr, conv(value)) return setter # classes for internal use class interface(object): """Class for internal use. It is mostly a data container used to easily pass information around; with some convenience methods.""" # information for other parts of the code changeable_attributes = ["name", "mtu", "lladdr", "broadcast", "up", "multicast", "arp"] # Index should be read-only index = property(_make_getter("_index")) up = property(_make_getter("_up"), _make_setter("_up", _any_to_bool)) mtu = property(_make_getter("_mtu"), _make_setter("_mtu", _positive)) lladdr = property(_make_getter("_lladdr"), _make_setter("_lladdr", _fix_lladdr)) arp = property(_make_getter("_arp"), _make_setter("_arp", _any_to_bool)) multicast = property(_make_getter("_mc"), _make_setter("_mc", _any_to_bool)) def __init__(self, index = None, name = None, up = None, mtu = None, lladdr = None, broadcast = None, multicast = None, arp = None): self._index = _positive(index) if index is not None else None self.name = name self.up = up self.mtu = mtu self.lladdr = lladdr self.broadcast = broadcast self.multicast = multicast self.arp = arp def __repr__(self): s = "%s.%s(index = %s, name = %s, up = %s, mtu = %s, lladdr = %s, " s += "broadcast = %s, multicast = %s, arp = %s)" return s % (self.__module__, self.__class__.__name__, self.index.__repr__(), self.name.__repr__(), self.up.__repr__(), self.mtu.__repr__(), self.lladdr.__repr__(), self.broadcast.__repr__(), self.multicast.__repr__(), self.arp.__repr__()) def __sub__(self, o): """Compare attributes and return a new object with just the attributes that differ set (with the value they have in the first operand). The index remains equal to the first operand.""" name = None if self.name == o.name else self.name up = None if self.up == o.up else self.up mtu = None if self.mtu == o.mtu else self.mtu lladdr = None if self.lladdr == o.lladdr else self.lladdr broadcast = None if self.broadcast == o.broadcast else self.broadcast multicast = None if self.multicast == o.multicast else self.multicast arp = None if self.arp == o.arp else self.arp return self.__class__(self.index, name, up, mtu, lladdr, broadcast, multicast, arp) def copy(self): return copy.copy(self) class bridge(interface): changeable_attributes = interface.changeable_attributes + ["stp", "forward_delay", "hello_time", "ageing_time", "max_age"] # Index should be read-only stp = property(_make_getter("_stp"), _make_setter("_stp", _any_to_bool)) forward_delay = property(_make_getter("_forward_delay"), _make_setter("_forward_delay", float)) hello_time = property(_make_getter("_hello_time"), _make_setter("_hello_time", float)) ageing_time = property(_make_getter("_ageing_time"), _make_setter("_ageing_time", float)) max_age = property(_make_getter("_max_age"), _make_setter("_max_age", float)) @classmethod def upgrade(cls, iface, *kargs, **kwargs): """Upgrade a interface to a bridge.""" return cls(iface.index, iface.name, iface.up, iface.mtu, iface.lladdr, iface.broadcast, iface.multicast, iface.arp, *kargs, **kwargs) def __init__(self, index = None, name = None, up = None, mtu = None, lladdr = None, broadcast = None, multicast = None, arp = None, stp = None, forward_delay = None, hello_time = None, ageing_time = None, max_age = None): super(bridge, self).__init__(index, name, up, mtu, lladdr, broadcast, multicast, arp) self.stp = stp self.forward_delay = forward_delay self.hello_time = hello_time self.ageing_time = ageing_time self.max_age = max_age def __repr__(self): s = "%s.%s(index = %s, name = %s, up = %s, mtu = %s, lladdr = %s, " s += "broadcast = %s, multicast = %s, arp = %s, stp = %s, " s += "forward_delay = %s, hello_time = %s, ageing_time = %s, " s += "max_age = %s)" return s % (self.__module__, self.__class__.__name__, self.index.__repr__(), self.name.__repr__(), self.up.__repr__(), self.mtu.__repr__(), self.lladdr.__repr__(), self.broadcast.__repr__(), self.multicast.__repr__(), self.arp.__repr__(), self.stp.__repr__(), self.forward_delay.__repr__(), self.hello_time.__repr__(), self.ageing_time.__repr__(), self.max_age.__repr__()) def __sub__(self, o): r = super(bridge, self).__sub__(o) if type(o) == interface: return r r.stp = None if self.stp == o.stp else self.stp r.hello_time = None if self.hello_time == o.hello_time else \ self.hello_time r.forward_delay = None if self.forward_delay == o.forward_delay else \ self.forward_delay r.ageing_time = None if self.ageing_time == o.ageing_time else \ self.ageing_time r.max_age = None if self.max_age == o.max_age else self.max_age return r class address(object): """Class for internal use. It is mostly a data container used to easily pass information around; with some convenience methods. __eq__ and __hash__ are defined just to be able to easily find duplicated addresses.""" # broadcast is not taken into account for differentiating addresses def __eq__(self, o): if not isinstance(o, address): return False return (self.family == o.family and self.address == o.address and self.prefix_len == o.prefix_len) def __hash__(self): h = (self.address.__hash__() ^ self.prefix_len.__hash__() ^ self.family.__hash__()) return h class ipv4address(address): def __init__(self, address, prefix_len, broadcast): self.address = address self.prefix_len = int(prefix_len) self.broadcast = broadcast self.family = socket.AF_INET def __repr__(self): s = "%s.%s(address = %s, prefix_len = %d, broadcast = %s)" return s % (self.__module__, self.__class__.__name__, self.address.__repr__(), self.prefix_len, self.broadcast.__repr__()) class ipv6address(address): def __init__(self, address, prefix_len): self.address = address self.prefix_len = int(prefix_len) self.family = socket.AF_INET6 def __repr__(self): s = "%s.%s(address = %s, prefix_len = %d)" return s % (self.__module__, self.__class__.__name__, self.address.__repr__(), self.prefix_len) class route(object): tipes = ["unicast", "local", "broadcast", "multicast", "throw", "unreachable", "prohibit", "blackhole", "nat"] tipe = property(_make_getter("_tipe", tipes.__getitem__), _make_setter("_tipe", tipes.index)) prefix = property(_make_getter("_prefix"), _make_setter("_prefix", _non_empty_str)) prefix_len = property(_make_getter("_plen"), lambda s, v: setattr(s, "_plen", int(v or 0))) nexthop = property(_make_getter("_nexthop"), _make_setter("_nexthop", _non_empty_str)) interface = property(_make_getter("_interface"), _make_setter("_interface", _positive)) metric = property(_make_getter("_metric"), lambda s, v: setattr(s, "_metric", int(v or 0))) def __init__(self, tipe = "unicast", prefix = None, prefix_len = 0, nexthop = None, interface = None, metric = 0): self.tipe = tipe self.prefix = prefix self.prefix_len = prefix_len self.nexthop = nexthop self.interface = interface self.metric = metric assert nexthop or interface def __repr__(self): s = "%s.%s(tipe = %s, prefix = %s, prefix_len = %s, nexthop = %s, " s += "interface = %s, metric = %s)" return s % (self.__module__, self.__class__.__name__, self.tipe.__repr__(), self.prefix.__repr__(), self.prefix_len.__repr__(), self.nexthop.__repr__(), self.interface.__repr__(), self.metric.__repr__()) def __eq__(self, o): if not isinstance(o, route): return False return (self.tipe == o.tipe and self.prefix == o.prefix and self.prefix_len == o.prefix_len and self.nexthop == o.nexthop and self.interface == o.interface and self.metric == o.metric) # helpers def _get_if_name(iface): if isinstance(iface, interface): if iface.name != None: return iface.name if isinstance(iface, str): return iface return get_if(iface).name # XXX: ideally this should be replaced by netlink communication # Interface handling # FIXME: try to lower the amount of calls to retrieve data!! def get_if_data(): """Gets current interface information. Returns a tuple (byidx, bynam) in which each element is a dictionary with the same data, but using different keys: interface indexes and interface names. In each dictionary, values are interface objects. """ ipdata = backticks([IP_PATH, "-o", "link", "list"]) byidx = {} bynam = {} for line in ipdata.split("\n"): if line == "": continue match = re.search(r'^(\d+):\s+(.*)', line) idx = int(match.group(1)) match = re.search(r'^(\d+): ([^@\s]+)(?:@\S+)?: <(\S+)> mtu (\d+) ' r'qdisc \S+.*link/\S+(?: ([0-9a-f:]+) ' r'brd ([0-9a-f:]+))?', line) flags = match.group(3).split(",") i = interface( index = match.group(1), name = match.group(2), up = "UP" in flags, mtu = match.group(4), lladdr = match.group(5), arp = not ("NOARP" in flags), broadcast = match.group(6), multicast = "MULTICAST" in flags) byidx[idx] = bynam[i.name] = i return byidx, bynam def get_if(iface): ifdata = get_if_data() if isinstance(iface, interface): if iface.index != None: return ifdata[0][iface.index] else: return ifdata[1][iface.name] if isinstance(iface, int): return ifdata[0][iface] return ifdata[1][iface] def create_if_pair(if1, if2): assert if1.name and if2.name cmd = [[], []] iface = [if1, if2] for i in (0, 1): cmd[i] = ["name", iface[i].name] if iface[i].lladdr: cmd[i] += ["address", iface[i].lladdr] if iface[i].broadcast: cmd[i] += ["broadcast", iface[i].broadcast] if iface[i].mtu: cmd[i] += ["mtu", str(iface[i].mtu)] cmd = [IP_PATH, "link", "add"] + cmd[0] + ["type", "veth", "peer"] + cmd[1] execute(cmd) try: set_if(if1) set_if(if2) except: (t, v, bt) = sys.exc_info() try: del_if(if1) del_if(if2) except: pass raise t, v, bt interfaces = get_if_data()[1] return interfaces[if1.name], interfaces[if2.name] def del_if(iface): ifname = _get_if_name(iface) execute([IP_PATH, "link", "del", ifname]) def set_if(iface, recover = True): def do_cmds(cmds, orig_iface): for c in cmds: try: execute(c) except: if recover: set_if(orig_iface, recover = False) # rollback raise orig_iface = get_if(iface) diff = iface - orig_iface # Only set what's needed # Name goes first if diff.name: _ils = [IP_PATH, "link", "set", "dev"] cmds = [_ils + [orig_iface.name, "name", diff.name]] if orig_iface.up: # iface needs to be down cmds = [_ils + [orig_iface.name, "down"], cmds[0], _ils + [diff.name, "up"]] do_cmds(cmds, orig_iface) # I need to use the new name after a name change, duh! _ils = [IP_PATH, "link", "set", "dev", diff.name or orig_iface.name] cmds = [] if diff.lladdr: if orig_iface.up: # iface needs to be down cmds.append(_ils + ["down"]) cmds.append(_ils + ["address", diff.lladdr]) if orig_iface.up and diff.up == None: # restore if it was up and it's not going to be set later cmds.append(_ils + ["up"]) if diff.mtu: cmds.append(_ils + ["mtu", str(diff.mtu)]) if diff.broadcast: cmds.append(_ils + ["broadcast", diff.broadcast]) if diff.multicast != None: cmds.append(_ils + ["multicast", "on" if diff.multicast else "off"]) if diff.arp != None: cmds.append(_ils + ["arp", "on" if diff.arp else "off"]) if diff.up != None: cmds.append(_ils + ["up" if diff.up else "down"]) do_cmds(cmds, orig_iface) def change_netns(iface, netns): ifname = _get_if_name(iface) execute([IP_PATH, "link", "set", "dev", ifname, "netns", str(netns)]) # Address handling def get_addr_data(): ipdata = backticks([IP_PATH, "addr", "list"]) byidx = {} bynam = {} current = None for line in ipdata.split("\n"): if line == "": continue match = re.search(r'^(\d+):\s+([^@\s]+)(?:@\S+)?:', line) if match: # First line of output. idx = int(match.group(1)) name = match.group(2) current = name if name in bynam: raise RuntimeError("Invalid `ip' command output") bynam[name] = byidx[idx] = [] continue if not current: raise RuntimeError("Invalid `ip' command output") match = re.search(r'^\s*inet ([0-9.]+)/(\d+)(?: brd ([0-9.]+))?', line) if match: bynam[current].append(ipv4address( address = match.group(1), prefix_len = match.group(2), broadcast = match.group(3))) continue match = re.search(r'^\s*inet6 ([0-9a-f:]+)/(\d+)', line) if match: bynam[current].append(ipv6address( address = match.group(1), prefix_len = match.group(2))) continue # Extra info, ignored. continue return byidx, bynam def add_addr(iface, address): ifname = _get_if_name(iface) addresses = get_addr_data()[1][ifname] assert address not in addresses cmd = [IP_PATH, "addr", "add", "dev", ifname, "local", "%s/%d" % (address.address, int(address.prefix_len))] if hasattr(address, "broadcast"): cmd += ["broadcast", address.broadcast if address.broadcast else "+"] execute(cmd) def del_addr(iface, address): ifname = _get_if_name(iface) addresses = get_addr_data()[1][ifname] assert address in addresses cmd = [IP_PATH, "addr", "del", "dev", ifname, "local", "%s/%d" % (address.address, int(address.prefix_len))] execute(cmd) def set_addr(iface, addresses, recover = True): ifname = _get_if_name(iface) addresses = get_addr_data()[1][ifname] to_remove = set(orig_addresses) - set(addresses) to_add = set(addresses) - set(orig_addresses) for a in to_remove: try: del_addr(ifname, a) except: if recover: set_addr(orig_addresses, recover = False) # rollback raise for a in to_add: try: add_addr(ifname, a) except: if recover: set_addr(orig_addresses, recover = False) # rollback raise # Bridge handling def _sysfs_read_br(brname): def readval(fname): f = file(fname) return f.readline().strip() p = "/sys/class/net/%s/bridge/" % brname p2 = "/sys/class/net/%s/brif/" % brname try: os.stat(p) except: return None return dict( stp = readval(p + "stp_state"), forward_delay = float(readval(p + "forward_delay")) / 100, hello_time = float(readval(p + "hello_time")) / 100, ageing_time = float(readval(p + "ageing_time")) / 100, max_age = float(readval(p + "max_age")) / 100, ports = os.listdir(p2)) def get_bridge_data(): # brctl stinks too much; it is better to directly use sysfs, it is # probably stable by now byidx = {} bynam = {} ports = {} ifdata = get_if_data() for iface in ifdata[0].values(): brdata = _sysfs_read_br(iface.name) if brdata == None: continue ports[iface.index] = [ifdata[1][x].index for x in brdata["ports"]] del brdata["ports"] bynam[iface.name] = byidx[iface.index] = \ bridge.upgrade(iface, **brdata) return byidx, bynam, ports def get_bridge(br): iface = get_if(br) brdata = _sysfs_read_br(iface.name) #ports = [ifdata[1][x].index for x in brdata["ports"]] del brdata["ports"] return bridge.upgrade(iface, **brdata) def create_bridge(br): if isinstance(br, str): br = interface(name = br) assert br.name execute([BRCTL_PATH, "addbr", br.name]) try: set_if(br) except: (t, v, bt) = sys.exc_info() try: del_bridge(br) except: pass raise t, v, bt return get_if_data()[1][br.name] def del_bridge(br): brname = _get_if_name(br) execute([BRCTL_PATH, "delbr", brname]) def set_bridge(br, recover = True): def saveval(fname, val): f = file(fname, "w") f.write(str(val)) f.close() def do_cmds(basename, cmds, orig_br): for n, v in cmds: try: saveval(basename + n, v) except: if recover: set_bridge(orig_br, recover = False) # rollback set_if(orig_br, recover = False) # rollback raise orig_br = get_bridge(br) diff = br - orig_br # Only set what's needed cmds = [] if diff.stp != None: cmds.append(("stp_state", int(diff.stp))) if diff.forward_delay != None: cmds.append(("forward_delay", int(diff.forward_delay))) if diff.hello_time != None: cmds.append(("hello_time", int(diff.hello_time))) if diff.ageing_time != None: cmds.append(("ageing_time", int(diff.ageing_time))) if diff.max_age != None: cmds.append(("max_age", int(diff.max_age))) set_if(diff) name = diff.name if diff.name != None else orig_br.name do_cmds("/sys/class/net/%s/bridge/" % name, cmds, orig_br) def add_bridge_port(br, iface): ifname = _get_if_name(iface) brname = _get_if_name(br) execute([BRCTL_PATH, "addif", brname, ifname]) def del_bridge_port(br, iface): ifname = _get_if_name(iface) brname = _get_if_name(br) execute([BRCTL_PATH, "delif", brname, ifname]) # Routing def get_all_route_data(): ipdata = backticks([IP_PATH, "-o", "route", "list"]) # "table", "all" ipdata += backticks([IP_PATH, "-o", "-f", "inet6", "route", "list"]) ifdata = get_if_data()[1] ret = [] for line in ipdata.split("\n"): if line == "": continue match = re.match(r'(?:(unicast|local|broadcast|multicast|throw|' + r'unreachable|prohibit|blackhole|nat) )?' + r'(\S+)(?: via (\S+))? dev (\S+).*(?: metric (\d+))?', line) if not match: raise RuntimeError("Invalid output from `ip route': `%s'" % line) tipe = match.group(1) or "unicast" prefix = match.group(2) nexthop = match.group(3) interface = ifdata[match.group(4)] metric = match.group(5) if prefix == "default" or re.search(r'/0$', prefix): prefix = None prefix_len = 0 else: match = re.match(r'([0-9a-f:.]+)(?:/(\d+))?$', prefix) prefix = match.group(1) prefix_len = int(match.group(2) or 32) ret.append(route(tipe, prefix, prefix_len, nexthop, interface.index, metric)) return ret def get_route_data(): # filter out non-unicast routes return [x for x in get_all_route_data() if x.tipe == "unicast"] def add_route(route): # Cannot really test this #if route in get_all_route_data(): # raise ValueError("Route already exists") _add_del_route("add", route) def del_route(route): # Cannot really test this #if route not in get_all_route_data(): # raise ValueError("Route does not exist") _add_del_route("del", route) def _add_del_route(action, route): cmd = [IP_PATH, "route", action] if route.tipe != "unicast": cmd += [route.tipe] if route.prefix: cmd += ["%s/%d" % (route.prefix, route.prefix_len)] else: cmd += ["default"] if route.nexthop: cmd += ["via", route.nexthop] if route.interface: cmd += ["dev", _get_if_name(route.interface)] execute(cmd) # TC stuff def get_tc_tree(): tcdata = backticks([TC_PATH, "qdisc", "show"]) data = {} for line in tcdata.split("\n"): if line == "": continue match = re.match(r'qdisc (\S+) ([0-9a-f]+):[0-9a-f]* dev (\S+) ' + r'(?:parent ([0-9a-f]*):[0-9a-f]*|root)\s*(.*)', line) if not match: raise RuntimeError("Invalid output from `tc qdisc': `%s'" % line) qdisc = match.group(1) handle = match.group(2) iface = match.group(3) parent = match.group(4) # or None extra = match.group(5) if parent == "": # XXX: Still not sure what is this, shows in newer kernels for wlan # interfaces. continue if iface not in data: data[iface] = {} if parent not in data[iface]: data[iface][parent] = [] data[iface][parent].append([handle, qdisc, parent, extra]) tree = {} for iface in data: def gen_tree(data, data_node): children = [] node = {"handle": data_node[0], "qdisc": data_node[1], "extra": data_node[3], "children": []} if data_node[0] in data: for h in data[data_node[0]]: node["children"].append(gen_tree(data, h)) return node tree[iface] = gen_tree(data[iface], data[iface][None][0]) return tree _multipliers = {"M": 1000000, "K": 1000} _dividers = {"m": 1000, "u": 1000000} def _parse_netem_delay(line): ret = {} match = re.search(r'delay ([\d.]+)([mu]?)s(?: +([\d.]+)([mu]?)s)?' + r'(?: *([\d.]+)%)?(?: *distribution (\S+))?', line) if not match: return ret delay = float(match.group(1)) if match.group(2): delay /= _dividers[match.group(2)] ret["delay"] = delay if match.group(3): delay_jitter = float(match.group(3)) if match.group(4): delay_jitter /= _dividers[match.group(4)] ret["delay_jitter"] = delay_jitter if match.group(5): ret["delay_correlation"] = float(match.group(5)) / 100 if match.group(6): ret["delay_distribution"] = match.group(6) return ret def _parse_netem_loss(line): ret = {} match = re.search(r'loss ([\d.]+)%(?: *([\d.]+)%)?', line) if not match: return ret ret["loss"] = float(match.group(1)) / 100 if match.group(2): ret["loss_correlation"] = float(match.group(2)) / 100 return ret def _parse_netem_dup(line): ret = {} match = re.search(r'duplicate ([\d.]+)%(?: *([\d.]+)%)?', line) if not match: return ret ret["dup"] = float(match.group(1)) / 100 if match.group(2): ret["dup_correlation"] = float(match.group(2)) / 100 return ret def _parse_netem_corrupt(line): ret = {} match = re.search(r'corrupt ([\d.]+)%(?: *([\d.]+)%)?', line) if not match: return ret ret["corrupt"] = float(match.group(1)) / 100 if match.group(2): ret["corrupt_correlation"] = float(match.group(2)) / 100 return ret def get_tc_data(): tree = get_tc_tree() ifdata = get_if_data() ret = {} for i in ifdata[0]: ret[i] = {"qdiscs": {}} if ifdata[0][i].name not in tree: continue node = tree[ifdata[0][i].name] if not node["children"]: if node["qdisc"] in ("mq", "pfifo_fast", "noqueue") \ or node["qdisc"][1:] == "fifo": continue if node["qdisc"] == "netem": tbf = None netem = node["extra"], node["handle"] elif node["qdisc"] == "tbf": tbf = node["extra"], node["handle"] netem = None else: ret[i] = "foreign" continue else: if node["qdisc"] != "tbf" or len(node["children"]) != 1 or \ node["children"][0]["qdisc"] != "netem" or \ node["children"][0]["children"]: ret[i] = "foreign" continue tbf = node["extra"], node["handle"] netem = node["children"][0]["extra"], \ node["children"][0]["handle"] if tbf: ret[i]["qdiscs"]["tbf"] = tbf[1] match = re.search(r'rate (\d+)([MK]?)bit', tbf[0]) if not match: ret[i] = "foreign" continue bandwidth = int(match.group(1)) if match.group(2): bandwidth *= _multipliers[match.group(2)] ret[i]["bandwidth"] = bandwidth if netem: ret[i]["qdiscs"]["netem"] = netem[1] ret[i].update(_parse_netem_delay(netem[0])) ret[i].update(_parse_netem_loss(netem[0])) ret[i].update(_parse_netem_dup(netem[0])) ret[i].update(_parse_netem_corrupt(netem[0])) return ret, ifdata[0], ifdata[1] def clear_tc(iface): iface = get_if(iface) tcdata = get_tc_data()[0] if tcdata[iface.index] == None: return # Any other case, we clean execute([TC_PATH, "qdisc", "del", "dev", iface.name, "root"]) def set_tc(iface, bandwidth = None, delay = None, delay_jitter = None, delay_correlation = None, delay_distribution = None, loss = None, loss_correlation = None, dup = None, dup_correlation = None, corrupt = None, corrupt_correlation = None): use_netem = bool(delay or delay_jitter or delay_correlation or delay_distribution or loss or loss_correlation or dup or dup_correlation or corrupt or corrupt_correlation) iface = get_if(iface) tcdata, ifdata = get_tc_data()[0:2] commands = [] if tcdata[iface.index] == 'foreign': # Avoid the overhead of calling tc+ip again commands.append([TC_PATH, "qdisc", "del", "dev", iface.name, "root"]) tcdata[iface.index] = {'qdiscs': []} has_netem = 'netem' in tcdata[iface.index]['qdiscs'] has_tbf = 'tbf' in tcdata[iface.index]['qdiscs'] if not bandwidth and not use_netem: if has_netem or has_tbf: clear_tc(iface) return if has_netem == use_netem and has_tbf == bool(bandwidth): cmd = "change" else: # Too much work to do better :) if has_netem or has_tbf: commands.append([TC_PATH, "qdisc", "del", "dev", iface.name, "root"]) cmd = "add" if bandwidth: rate = "%dbit" % int(bandwidth) mtu = ifdata[iface.index].mtu burst = max(mtu, int(bandwidth) / HZ) limit = burst * 2 # FIXME? handle = "1:" if cmd == "change": handle = "%d:" % int(tcdata[iface.index]["qdiscs"]["tbf"]) command = [TC_PATH, "qdisc", cmd, "dev", iface.name, "root", "handle", handle, "tbf", "rate", rate, "limit", str(limit), "burst", str(burst)] commands.append(command) if use_netem: handle = "2:" if cmd == "change": handle = "%d:" % int(tcdata[iface.index]["qdiscs"]["netem"]) command = [TC_PATH, "qdisc", cmd, "dev", iface.name, "handle", handle] if bandwidth: parent = "1:" if cmd == "change": parent = "%d:" % int(tcdata[iface.index]["qdiscs"]["tbf"]) command += ["parent", parent] else: command += ["root"] command += ["netem"] if delay: command += ["delay", "%fs" % delay] if delay_jitter: command += ["%fs" % delay_jitter] if delay_correlation: if not delay_jitter: raise ValueError("delay_correlation requires delay_jitter") command += ["%f%%" % (delay_correlation * 100)] if delay_distribution: if not delay_jitter: raise ValueError("delay_distribution requires delay_jitter") command += ["distribution", delay_distribution] if loss: command += ["loss", "%f%%" % (loss * 100)] if loss_correlation: command += ["%f%%" % (loss_correlation * 100)] if dup: command += ["duplicate", "%f%%" % (dup * 100)] if dup_correlation: command += ["%f%%" % (dup_correlation * 100)] if corrupt: command += ["corrupt", "%f%%" % (corrupt * 100)] if corrupt_correlation: command += ["%f%%" % (corrupt_correlation * 100)] commands.append(command) for c in commands: execute(c) def create_tap(iface, use_pi = False, tun = False): """Creates a tap/tun device and returns the associated file descriptor""" if isinstance(iface, str): iface = interface(name = iface) assert iface.name IFF_TUN = 0x0001 IFF_TAP = 0x0002 IFF_NO_PI = 0x1000 TUNSETIFF = 0x400454ca if tun: mode = IFF_TUN else: mode = IFF_TAP if not use_pi: mode |= IFF_NO_PI fd = os.open("/dev/net/tun", os.O_RDWR) err = fcntl.ioctl(fd, TUNSETIFF, struct.pack("16sH", iface.name, mode)) if err < 0: os.close(fd) raise RuntimeError("Could not configure device %s" % iface.name) try: set_if(iface) except: os.close(fd) raise interfaces = get_if_data()[1] return interfaces[iface.name], fd nemu-0.3.1/src/nemu/node.py000066400000000000000000000171171300327754000155210ustar00rootroot00000000000000# vim:ts=4:sw=4:et:ai:sts=4 # -*- coding: utf-8 -*- # Copyright 2010, 2011 INRIA # Copyright 2011 Martín Ferrari # # This file is part of Nemu. # # Nemu is free software: you can redistribute it and/or modify it under the # terms of the GNU General Public License version 2, as published by the Free # Software Foundation. # # Nemu is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR # A PARTICULAR PURPOSE. See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along with # Nemu. If not, see . import os, socket, sys, traceback, unshare, weakref from nemu.environ import * import nemu.interface, nemu.protocol, nemu.subprocess_ __all__ = ['Node', 'get_nodes', 'import_if'] class Node(object): _nodes = weakref.WeakValueDictionary() _nextnode = 0 @staticmethod def get_nodes(): s = sorted(Node._nodes.items(), key = lambda x: x[0]) return [x[1] for x in s] def __init__(self, nonetns = False, forward_X11 = False): """Create a new node in the emulation. Implemented as a separate process in a new network name space. Requires root privileges to run. If nonetns is true, the network name space is not created and can be run as a normal user, for testing.""" # Initialize attributes, in case something fails during __init__ self._pid = self._slave = None self._processes = weakref.WeakValueDictionary() self._interfaces = weakref.WeakValueDictionary() self._auto_interfaces = [] # just to keep them alive! fd, pid = _start_child(nonetns) self._pid = pid debug("Node(0x%x).__init__(), pid = %s" % (id(self), pid)) self._slave = nemu.protocol.Client(fd, fd) if forward_X11: self._slave.enable_x11_forwarding() Node._nodes[Node._nextnode] = self Node._nextnode += 1 # Bring loopback up if not nonetns: self.get_interface("lo").up = True def __del__(self): debug("Node(0x%x).__del__()" % id(self)) self.destroy() def destroy(self): if not self._pid: return debug("Node(0x%x).destroy()" % id(self)) for p in self._processes.values(): p.destroy() self._processes.clear() # Use get_interfaces to force a rescan for i in self.get_interfaces(): i.destroy() self._interfaces.clear() if self._slave: self._slave.shutdown() exitcode = eintr_wrapper(os.waitpid, self._pid, 0)[1] if exitcode != 0: error("Node(0x%x) process %d exited with non-zero status: %d" % (id(self), self._pid, exitcode)) self._pid = self._slave = None @property def pid(self): return self._pid # Subprocesses def _add_subprocess(self, subprocess): self._processes[subprocess.pid] = subprocess def Subprocess(self, *kargs, **kwargs): return nemu.subprocess_.Subprocess(self, *kargs, **kwargs) def Popen(self, *kargs, **kwargs): return nemu.subprocess_.Popen(self, *kargs, **kwargs) def system(self, *kargs, **kwargs): return nemu.subprocess_.system(self, *kargs, **kwargs) def backticks(self, *kargs, **kwargs): return nemu.subprocess_.backticks(self, *kargs, **kwargs) def backticks_raise(self, *kargs, **kwargs): return nemu.subprocess_.backticks_raise(self, *kargs, **kwargs) # Interfaces def _add_interface(self, interface): self._interfaces[interface.index] = interface def add_if(self, **kwargs): i = nemu.interface.NodeInterface(self) for k, v in kwargs.items(): setattr(i, k, v) return i def add_tap(self, use_pi = False, **kwargs): i = nemu.interface.TapNodeInterface(self, use_pi) for k, v in kwargs.items(): setattr(i, k, v) return i def add_tun(self, use_pi = False, **kwargs): i = nemu.interface.TunNodeInterface(self, use_pi) for k, v in kwargs.items(): setattr(i, k, v) return i def import_if(self, interface): return nemu.interface.ImportedNodeInterface(self, interface) def del_if(self, iface): """Doesn't destroy the interface if it wasn't created by us.""" del self._interfaces[iface.index] iface.destroy() def get_interface(self, name): return [i for i in self.get_interfaces() if i.name == name][0] def get_interfaces(self): if not self._slave: return [] ifaces = self._slave.get_if_data() for i in ifaces: if i not in self._interfaces: iface = nemu.interface.ImportedNodeInterface(self, i, migrate = False) self._auto_interfaces.append(iface) # keep it referenced! self._interfaces[i] = iface # by the way, clean up _interfaces for i in list(self._interfaces): # copy before deleting! if i not in ifaces: notice("Node(0x%x): interface #%d went away." % (id(self), i)) self._interfaces[i].destroy() del self._interfaces[i] return sorted(self._interfaces.values(), key = lambda x: x.index) def route(self, tipe = 'unicast', prefix = None, prefix_len = 0, nexthop = None, interface = None, metric = 0): return nemu.iproute.route(tipe, prefix, prefix_len, nexthop, interface.index if interface else None, metric) def add_route(self, *args, **kwargs): # Accepts either a route object or all its constructor's parameters if len(args) == 1 and not kwargs: r = args[0] else: r = self.route(*args, **kwargs) return self._slave.add_route(r) def del_route(self, *args, **kwargs): if len(args) == 1 and not kwargs: r = args[0] else: r = self.route(*args, **kwargs) return self._slave.del_route(r) def get_routes(self): return self._slave.get_route_data() # Handle the creation of the child; parent gets (fd, pid), child creates and # runs a Server(); never returns. # Requires CAP_SYS_ADMIN privileges to run. def _start_child(nonetns): # Create socket pair to communicate (s0, s1) = socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM, 0) # Spawn a child that will run in a loop pid = os.fork() if pid: s1.close() return (s0, pid) # FIXME: clean up signal handers, atexit functions, etc. try: # pragma: no cover # coverage doesn't seem to understand fork s0.close() srv = nemu.protocol.Server(s1, s1) if not nonetns: # create new name space unshare.unshare(unshare.CLONE_NEWNET) # Enable packet forwarding execute([SYSCTL_PATH, '-w', 'net.ipv4.ip_forward=1']) execute([SYSCTL_PATH, '-w', 'net.ipv6.conf.default.forwarding=1']) srv.run() except BaseException, e: s = "Slave node aborting: %s\n" % str(e) sep = "=" * 70 + "\n" sys.stderr.write(s + sep) traceback.print_exc(file=sys.stdout) sys.stderr.write(sep) try: # try to pass the error to parent, if possible s1.send("500 " + s) except: pass os._exit(1) os._exit(0) # pragma: no cover # NOTREACHED get_nodes = Node.get_nodes import_if = nemu.interface.ImportedInterface nemu-0.3.1/src/nemu/protocol.py000066400000000000000000000770441300327754000164420ustar00rootroot00000000000000# vim:ts=4:sw=4:et:ai:sts=4 # -*- coding: utf-8 -*- # Copyright 2010, 2011 INRIA # Copyright 2011 Martín Ferrari # # This file is part of Nemu. # # Nemu is free software: you can redistribute it and/or modify it under the # terms of the GNU General Public License version 2, as published by the Free # Software Foundation. # # Nemu is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR # A PARTICULAR PURPOSE. See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along with # Nemu. If not, see . import base64, errno, os, passfd, re, select, signal, socket, sys, tempfile import time, traceback, unshare import nemu.subprocess_, nemu.iproute from nemu.environ import * try: from cPickle import loads, dumps except: from pickle import loads, dumps # ============================================================================ # Server-side protocol implementation # # Protocol definition # ------------------- # # First key: command # Second key: sub-command or None # Value: pair of format strings for mandatory and optional parameters. # The format string is a chain of "s" for string and "i" for integer _proto_commands = { "QUIT": { None: ("", "") }, "HELP": { None: ("", "") }, "X11": { "SET": ("ss", ""), "SOCK": ("", "") }, "IF": { "LIST": ("", "i"), "SET": ("iss", "s*"), "RTRN": ("ii", ""), "DEL": ("i", "") }, "ADDR": { "LIST": ("", "i"), "ADD": ("isi", "s"), "DEL": ("iss", "s") }, "ROUT": { "LIST": ("", ""), "ADD": ("bbibii", ""), "DEL": ("bbibii", "") }, "PROC": { "CRTE": ("b", "b*"), "POLL": ("i", ""), "WAIT": ("i", ""), "KILL": ("i", "i") }, } # Commands valid only after PROC CRTE _proc_commands = { "HELP": { None: ("", "") }, "QUIT": { None: ("", "") }, "PROC": { "USER": ("b", ""), "CWD": ("b", ""), "ENV": ("bb", "b*"), "SIN": ("", ""), "SOUT": ("", ""), "SERR": ("", ""), "RUN": ("", ""), "ABRT": ("", ""), } } KILL_WAIT = 3 # seconds class Server(object): """Class that implements the communication protocol and dispatches calls to the required functions. Also works as the main loop for the slave process.""" def __init__(self, rfd, wfd): debug("Server(0x%x).__init__()" % id(self)) # Dictionary of valid commands self._commands = _proto_commands # Flag to stop the server self._closed = False # Set to keep track of started processes self._children = set() # Buffer and flag for PROC mode self._proc = None # temporary xauth files self._xauthfiles = {} # X11 forwarding info self._xfwd = None self._xsock = None self._rfd = _get_file(rfd, "r") self._wfd = _get_file(wfd, "w") def clean(self): try: for pid in self._children: # -PID to kill to whole process group os.kill(-pid, signal.SIGTERM) now = time.time() while time.time() - now < KILL_WAIT: ch = set(self._children) for pid in ch: try: if nemu.subprocess_.poll(pid): self._children.remove(pid) except OSError, e: if e.errno == errno.ECHILD: self._children.remove(pid) else: raise if not ch: break time.sleep(0.1) for pid in self._children: warning("Killing forcefully process %d." % pid) # -PID to kill to whole process group os.kill(-pid, signal.SIGKILL) for pid in self._children: try: nemu.subprocess_.poll(pid) except OSError, e: if e.errno != errno.ECHILD: raise finally: for f in self._xauthfiles.values(): try: os.unlink(f) except: pass def reply(self, code, text): "Send back a reply to the client; handle multiline messages" if not hasattr(text, '__iter__'): text = [ text ] clean = [] # Split lines with embedded \n for i in text: clean.extend(i.splitlines()) for i in range(len(clean) - 1): s = str(code) + "-" + clean[i] + "\n" self._wfd.write(s) debug(" %s" % s) s = str(code) + " " + clean[-1] + "\n" self._wfd.write(s) debug(" %s" % s) return def readline(self): "Read a line from the socket and detect connection break-up." # FIXME: should use the eintr_wrapper from environ; why was I using # readline instead of read? while True: try: line = self._rfd.readline() except IOError, e: line = None if e.errno == errno.EINTR: continue else: raise break if not line: self._closed = True return None debug(" %s" % line) return line.rstrip() def readcmd(self): """Main entry point: read and parse a line from the client, handle argument validation and return a tuple (function, command_name, arguments)""" line = self.readline() if not line: return None args = line.split() cmd1 = args[0].upper() if cmd1 not in self._commands: self.reply(500, "Unknown command %s." % cmd1) return None del args[0] cmd2 = None subcommands = self._commands[cmd1] if subcommands.keys() != [ None ]: if len(args) < 1: self.reply(500, "Incomplete command.") return None cmd2 = args[0].upper() del args[0] if cmd2 and cmd2 not in subcommands: self.reply(500, "Unknown sub-command for %s: %s." % (cmd1, cmd2)) return None (mandatory, optional) = subcommands[cmd2] argstemplate = mandatory + optional if cmd2: cmdname = "%s %s" % (cmd1, cmd2) funcname = "do_%s_%s" % (cmd1, cmd2) else: cmdname = cmd1 funcname = "do_%s" % cmd1 if not hasattr(self, funcname): # pragma: no cover self.reply(500, "Not implemented.") return None if len(args) < len(mandatory): self.reply(500, "Missing mandatory arguments for %s." % cmdname) return None if (not argstemplate or argstemplate[-1] != "*") and \ len(args) > len(argstemplate): self.reply(500, "Too many arguments for %s." % cmdname) return None j = 0 for i in range(len(args)): if argstemplate[j] == '*': j = j - 1 if argstemplate[j] == 'i': try: args[i] = int(args[i]) except: self.reply(500, "Invalid parameter %s: must be an integer." % args[i]) return None elif argstemplate[j] == 'b': try: args[i] = _db64(args[i]) except TypeError: self.reply(500, "Invalid parameter: not base-64 encoded.") return None elif argstemplate[j] != 's': # pragma: no cover raise RuntimeError("Invalid argument template: %s" % _argstmpl) # Nothing done for "s" parameters j += 1 func = getattr(self, funcname) debug("Command: %s, args: %s" % (cmdname, args)) return (func, cmdname, args) def run(self): """Main loop; reads commands until the server is shut down or the connection is terminated.""" self.reply(220, "Hello."); while not self._closed: cmd = self.readcmd() if cmd == None: continue try: cmd[0](cmd[1], *cmd[2]) except: (t, v, tb) = sys.exc_info() v.child_traceback = "".join( traceback.format_exception(t, v, tb)) self.reply(550, ["# Exception data follows:", _b64(dumps(v, protocol = 2))]) try: self._rfd.close() self._wfd.close() except: pass self.clean() debug("Server(0x%x) exiting" % id(self)) # FIXME: cleanup # Commands implementation def do_HELP(self, cmdname): reply = ["Available commands:"] for c in sorted(self._commands): for sc in sorted(self._commands[c]): if sc: reply.append("%s %s" % (c, sc)) else: reply.append(c) self.reply(200, reply) def do_QUIT(self, cmdname): self.reply(221, "Sayounara."); self._closed = True def do_PROC_CRTE(self, cmdname, executable, *argv): self._proc = { 'executable': executable, 'argv': argv } self._commands = _proc_commands self.reply(200, "Entering PROC mode.") def do_PROC_USER(self, cmdname, user): self._proc['user'] = user self.reply(200, "Program will run as `%s'." % user) def do_PROC_CWD(self, cmdname, dir): self._proc['cwd'] = dir self.reply(200, "CWD set to `%s'." % dir) def do_PROC_ENV(self, cmdname, *env): if len(env) % 2: self.reply(500, "Invalid number of arguments for PROC ENV: must be even.") return self._proc['env'] = {} for i in range(len(env)/2): self._proc['env'][env[i * 2]] = env[i * 2 + 1] self.reply(200, "%d environment definition(s) read." % (len(env) / 2)) def do_PROC_SIN(self, cmdname): self.reply(354, "Pass the file descriptor now, with `%s\\n' as payload." % cmdname) try: fd, payload = passfd.recvfd(self._rfd, len(cmdname) + 1) except (IOError, RuntimeError), e: self.reply(500, "Error receiving FD: %s" % str(e)) return if payload[0:len(cmdname)] != cmdname: self.reply(500, "Invalid payload: %s." % payload) return m = {'PROC SIN': 'stdin', 'PROC SOUT': 'stdout', 'PROC SERR': 'stderr'} self._proc[m[cmdname]] = fd self.reply(200, 'FD saved as %s.' % m[cmdname]) # Same code for the three commands do_PROC_SOUT = do_PROC_SERR = do_PROC_SIN def do_PROC_RUN(self, cmdname): params = self._proc params['close_fds'] = True # forced self._proc = None self._commands = _proto_commands if 'env' not in params: params['env'] = dict(os.environ) # copy xauth = None if self._xfwd: display, protoname, hexkey = self._xfwd user = params['user'] if 'user' in params else None try: fd, xauth = tempfile.mkstemp() os.close(fd) # stupid xauth format: needs the 'hostname' for local # connections execute([XAUTH_PATH, "-f", xauth, "add", "%s/unix:%d" % (socket.gethostname(), display), protoname, hexkey]) if user: user, uid, gid = nemu.subprocess_.get_user(user) os.chown(xauth, uid, gid) params['env']['DISPLAY'] = "127.0.0.1:%d" % display params['env']['XAUTHORITY'] = xauth except Exception, e: warning("Cannot forward X: %s" % e) try: os.unlink(xauth) except: pass else: if 'DISPLAY' in params['env']: del params['env']['DISPLAY'] try: chld = nemu.subprocess_.spawn(**params) finally: # I can close the fds now for d in ('stdin', 'stdout', 'stderr'): if d in params: os.close(params[d]) self._children.add(chld) self._xauthfiles[chld] = xauth self.reply(200, "%d running." % chld) def do_PROC_ABRT(self, cmdname): self._proc = None self._commands = _proto_commands self.reply(200, "Aborted.") def do_PROC_POLL(self, cmdname, pid): if pid not in self._children: self.reply(500, "Process does not exist.") return if cmdname == 'PROC POLL': ret = nemu.subprocess_.poll(pid) else: ret = nemu.subprocess_.wait(pid) if ret != None: self._children.remove(pid) if pid in self._xauthfiles: try: os.unlink(self._xauthfiles[pid]) except: pass del self._xauthfiles[pid] self.reply(200, "%d exitcode." % ret) else: self.reply(450, "Not finished yet.") # Same code for the two commands do_PROC_WAIT = do_PROC_POLL def do_PROC_KILL(self, cmdname, pid, sig): if pid not in self._children: self.reply(500, "Process does not exist.") return if signal: # -PID to kill to whole process group os.kill(-pid, sig) else: os.kill(-pid, signal.SIGTERM) self.reply(200, "Process signalled.") def do_IF_LIST(self, cmdname, ifnr = None): if ifnr == None: ifdata = nemu.iproute.get_if_data()[0] else: ifdata = nemu.iproute.get_if(ifnr) self.reply(200, ["# Interface data follows.", _b64(dumps(ifdata, protocol = 2))]) def do_IF_SET(self, cmdname, ifnr, *args): if len(args) % 2: self.reply(500, "Invalid number of arguments for IF SET: must be even.") return d = {'index': ifnr} for i in range(len(args) / 2): d[str(args[i * 2])] = args[i * 2 + 1] iface = nemu.iproute.interface(**d) nemu.iproute.set_if(iface) self.reply(200, "Done.") def do_IF_RTRN(self, cmdname, ifnr, ns): nemu.iproute.change_netns(ifnr, ns) self.reply(200, "Done.") def do_IF_DEL(self, cmdname, ifnr): nemu.iproute.del_if(ifnr) self.reply(200, "Done.") def do_ADDR_LIST(self, cmdname, ifnr = None): addrdata = nemu.iproute.get_addr_data()[0] if ifnr != None: addrdata = addrdata[ifnr] self.reply(200, ["# Address data follows.", _b64(dumps(addrdata, protocol = 2))]) def do_ADDR_ADD(self, cmdname, ifnr, address, prefixlen, broadcast = None): if address.find(":") < 0: # crude, I know a = nemu.iproute.ipv4address(address, prefixlen, broadcast) else: a = nemu.iproute.ipv6address(address, prefixlen) nemu.iproute.add_addr(ifnr, a) self.reply(200, "Done.") def do_ADDR_DEL(self, cmdname, ifnr, address, prefixlen): if address.find(":") < 0: # crude, I know a = nemu.iproute.ipv4address(address, prefixlen, None) else: a = nemu.iproute.ipv6address(address, prefixlen) nemu.iproute.del_addr(ifnr, a) self.reply(200, "Done.") def do_ROUT_LIST(self, cmdname): rdata = nemu.iproute.get_route_data() self.reply(200, ["# Routing data follows.", _b64(dumps(rdata, protocol = 2))]) def do_ROUT_ADD(self, cmdname, tipe, prefix, prefixlen, nexthop, ifnr, metric): nemu.iproute.add_route(nemu.iproute.route(tipe, prefix, prefixlen, nexthop, ifnr or None, metric)) self.reply(200, "Done.") def do_ROUT_DEL(self, cmdname, tipe, prefix, prefixlen, nexthop, ifnr, metric): nemu.iproute.del_route(nemu.iproute.route(tipe, prefix, prefixlen, nexthop, ifnr or None, metric)) self.reply(200, "Done.") def do_X11_SET(self, cmdname, protoname, hexkey): if not XAUTH_PATH: self.reply(500, "Impossible to forward X: xauth not present") return skt, port = None, None try: skt, port = find_listen_port(min_port = 6010, max_port = 6099) except: self.reply(500, "Cannot allocate a port for X forwarding.") return display = port - 6000 self.reply(200, "Socket created on port %d. Use X11 SOCK to get the " "file descriptor " "(fixed 1-byte payload before protocol response).") self._xfwd = display, protoname, hexkey self._xsock = skt def do_X11_SOCK(self, cmdname): if not self._xsock: self.reply(500, "X forwarding not set up.") return # Needs to be a separate command to handle synch & buffering issues try: passfd.sendfd(self._wfd, self._xsock.fileno(), "1") except: # need to fill the buffer on the other side, nevertheless self._wfd.write("1") self.reply(500, "Error sending file descriptor.") return self._xsock = None self.reply(200, "Will set up X forwarding.") # ============================================================================ # # Client-side protocol implementation. # class Client(object): """Client-side implementation of the communication protocol. Acts as a RPC service.""" def __init__(self, rfd, wfd): debug("Client(0x%x).__init__()" % id(self)) self._rfd = _get_file(rfd, "r") self._wfd = _get_file(wfd, "w") self._forwarder = None # Wait for slave to send banner self._read_and_check_reply() def __del__(self): debug("Client(0x%x).__del__()" % id(self)) self.shutdown() def _send_cmd(self, *args): if not self._wfd: raise RuntimeError("Client already shut down.") s = " ".join(map(str, args)) + "\n" self._wfd.write(s) def _read_reply(self): """Reads a (possibly multi-line) response from the server. Returns a tuple containing (code, text)""" if not self._rfd: raise RuntimeError("Client already shut down.") text = [] while True: line = eintr_wrapper(self._rfd.readline).rstrip() if not line: raise RuntimeError("Protocol error, empty line received") m = re.search(r'^(\d{3})([ -])(.*)', line) if not m: raise RuntimeError("Protocol error, read: %r" % line) status = m.group(1) text.append(m.group(3)) if m.group(2) == " ": break return (int(status), "\n".join(text)) def _read_and_check_reply(self, expected = 2): """Reads a response and raises an exception if the first digit of the code is not the expected value. If expected is not specified, it defaults to 2.""" code, text = self._read_reply() if code == 550: # exception e = loads(_db64(text.partition("\n")[2])) raise e if code / 100 != expected: raise RuntimeError("Error from slave: %d %s" % (code, text)) return text def shutdown(self): "Tell the client to quit." if not self._wfd: return debug("Client(0x%x).shutdown()" % id(self)) self._send_cmd("QUIT") self._read_and_check_reply() self._rfd.close() self._rfd = None self._wfd.close() self._wfd = None if self._forwarder: os.kill(self._forwarder, signal.SIGTERM) self._forwarder = None def _send_fd(self, name, fd): "Pass a file descriptor" self._send_cmd("PROC", name) self._read_and_check_reply(3) try: passfd.sendfd(self._wfd, fd, "PROC " + name) except: # need to fill the buffer on the other side, nevertheless self._wfd.write("=" * (len(name) + 5) + "\n") # And also read the expected error self._read_and_check_reply(5) raise self._read_and_check_reply() def spawn(self, argv, executable = None, stdin = None, stdout = None, stderr = None, cwd = None, env = None, user = None): """Start a subprocess in the slave; the interface resembles subprocess.Popen, but with less functionality. In particular stdin/stdout/stderr can only be None or a open file descriptor. See nemu.subprocess_.spawn for details.""" if executable == None: executable = argv[0] params = ["PROC", "CRTE", _b64(executable)] for i in argv: params.append(_b64(i)) self._send_cmd(*params) self._read_and_check_reply() # After this, if we get an error, we have to abort the PROC try: if user != None: self._send_cmd("PROC", "USER", _b64(user)) self._read_and_check_reply() if cwd != None: self._send_cmd("PROC", "CWD", _b64(cwd)) self._read_and_check_reply() if env != None: params = [] for k, v in env.items(): params.extend([_b64(k), _b64(v)]) self._send_cmd("PROC", "ENV", *params) self._read_and_check_reply() if stdin != None: self._send_fd("SIN", stdin) if stdout != None: self._send_fd("SOUT", stdout) if stderr != None: self._send_fd("SERR", stderr) except: self._send_cmd("PROC", "ABRT") self._read_and_check_reply() raise self._send_cmd("PROC", "RUN") pid = int(self._read_and_check_reply().split()[0]) return pid def poll(self, pid): """Equivalent to Popen.poll(), checks if the process has finished. Returns the exitcode if finished, None otherwise.""" self._send_cmd("PROC", "POLL", pid) code, text = self._read_reply() if code / 100 == 2: exitcode = int(text.split()[0]) return exitcode if code / 100 == 4: return None else: raise RuntimeError("Error on command: %d %s" % (code, text)) def wait(self, pid): """Equivalent to Popen.wait(). Waits for the process to finish and returns the exitcode.""" self._send_cmd("PROC", "WAIT", pid) text = self._read_and_check_reply() exitcode = int(text.split()[0]) return exitcode def signal(self, pid, sig = signal.SIGTERM): """Equivalent to Popen.send_signal(). Sends a signal to the child process; signal defaults to SIGTERM.""" if sig: self._send_cmd("PROC", "KILL", pid, sig) else: self._send_cmd("PROC", "KILL", pid) self._read_and_check_reply() def get_if_data(self, ifnr = None): if ifnr: self._send_cmd("IF", "LIST", ifnr) else: self._send_cmd("IF", "LIST") data = self._read_and_check_reply() return loads(_db64(data.partition("\n")[2])) def set_if(self, interface): cmd = ["IF", "SET", interface.index] for k in interface.changeable_attributes: v = getattr(interface, k) if v != None: cmd += [k, str(v)] self._send_cmd(*cmd) self._read_and_check_reply() def del_if(self, ifnr): self._send_cmd("IF", "DEL", ifnr) self._read_and_check_reply() def change_netns(self, ifnr, netns): self._send_cmd("IF", "RTRN", ifnr, netns) self._read_and_check_reply() def get_addr_data(self, ifnr = None): if ifnr: self._send_cmd("ADDR", "LIST", ifnr) else: self._send_cmd("ADDR", "LIST") data = self._read_and_check_reply() return loads(_db64(data.partition("\n")[2])) def add_addr(self, ifnr, address): if hasattr(address, "broadcast") and address.broadcast: self._send_cmd("ADDR", "ADD", ifnr, address.address, address.prefix_len, address.broadcast) else: self._send_cmd("ADDR", "ADD", ifnr, address.address, address.prefix_len) self._read_and_check_reply() def del_addr(self, ifnr, address): self._send_cmd("ADDR", "DEL", ifnr, address.address, address.prefix_len) self._read_and_check_reply() def get_route_data(self): self._send_cmd("ROUT", "LIST") data = self._read_and_check_reply() return loads(_db64(data.partition("\n")[2])) def add_route(self, route): self._add_del_route("ADD", route) def del_route(self, route): self._add_del_route("DEL", route) def _add_del_route(self, action, route): args = ["ROUT", action, _b64(route.tipe), _b64(route.prefix), route.prefix_len or 0, _b64(route.nexthop), route.interface or 0, route.metric or 0] self._send_cmd(*args) self._read_and_check_reply() def set_x11(self, protoname, hexkey): # Returns a socket ready to accept() connections self._send_cmd("X11", "SET", protoname, hexkey) self._read_and_check_reply() # Receive the socket self._send_cmd("X11", "SOCK") fd, payload = passfd.recvfd(self._rfd, 1) self._read_and_check_reply() skt = socket.fromfd(fd, socket.AF_INET, socket.SOCK_DGRAM) os.close(fd) # fromfd dup()'s return skt def enable_x11_forwarding(self): xinfo = _parse_display() if not xinfo: raise RuntimeError("Impossible to forward X: DISPLAY variable not " "set or invalid") xauthdpy, sock, addr = xinfo if not XAUTH_PATH: raise RuntimeError("Impossible to forward X: xauth not present") auth = backticks([XAUTH_PATH, "list", xauthdpy]) match = re.match(r"\S+\s+(\S+)\s+(\S+)\n", auth) if not match: raise RuntimeError("Impossible to forward X: invalid DISPLAY") protoname, hexkey = match.groups() server = self.set_x11(protoname, hexkey) self._forwarder = _spawn_x11_forwarder(server, sock, addr) def _b64(text): if text == None: # easier this way text = '' text = str(text) if len(text) == 0 or filter(lambda x: ord(x) <= ord(" ") or ord(x) > ord("z") or x == "=", text): return "=" + base64.b64encode(text) else: return text def _db64(text): if not text or text[0] != '=': return text return base64.b64decode(text[1:]) def _get_file(fd, mode): # Since fdopen insists on closing the fd on destruction, I need to dup() if hasattr(fd, "fileno"): nfd = os.dup(fd.fileno()) else: nfd = os.dup(fd) return os.fdopen(nfd, mode, 1) def _parse_display(): if "DISPLAY" not in os.environ: return None dpy = os.environ["DISPLAY"] match = re.search(r"^(.*):(\d+)(?:\.\d+)?$", dpy) if not match: return None host, number = match.groups() if host and host != "unix": sock = (socket.AF_INET, socket.SOCK_STREAM, 0) addr = (host, 6000 + int(number)) else: sock = (socket.AF_UNIX, socket.SOCK_STREAM, 0) addr = ("/tmp/.X11-unix/X%s" % number) # Xauth does not work with DISPLAYs of the form localhost:X. if host and host != "localhost": xauthdpy = dpy else: xauthdpy = "unix:%s" % number return xauthdpy, sock, addr def _spawn_x11_forwarder(server, xsock, xaddr): server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server.listen(10) # arbitrary pid = os.fork() if pid: return pid # XXX: clear signals, etc try: _x11_forwarder(server, xsock, xaddr) except: traceback.print_exc(file=sys.stderr) os._exit(1) def _x11_forwarder(server, xsock, xaddr): def clean(idx, toread, fd): # silently discards any buffer! fd1 = fd fd2 = idx[fd]["wr"] try: fd1.close() except: pass try: fd2.close() except: pass del idx[fd1] if fd1 in toread: toread.remove(fd1) del idx[fd2] if fd2 in toread: toread.remove(fd2) toread = set([server]) idx = {} while(True): towrite = [x["wr"] for x in idx.values() if x["buf"]] (rr, wr, er) = select.select(toread, towrite, []) if server in rr: xconn = socket.socket(*xsock) xconn.connect(xaddr) client, addr = server.accept() toread.add(client) toread.add(xconn) idx[client] = { "rd": client, "wr": xconn, "buf": [], "closed": False } idx[xconn] = { "rd": xconn, "wr": client, "buf": [], "closed": False } continue for fd in rr: chan = idx[fd] try: s = os.read(fd.fileno(), 4096) except OSError, e: if e.errno == errno.ECONNRESET: clean(idx, toread, fd) continue elif e.errno == errno.EINTR: continue else: raise if s == "": # fd closed for read toread.remove(fd) chan["closed"] = True if not chan["buf"]: # close the writing side try: chan["wr"].shutdown(socket.SHUT_WR) except: pass # might fail sometimes else: chan["buf"].append(s) for fd in wr: chan = idx[idx[fd]["wr"]] try: x = os.write(fd.fileno(), chan["buf"][0]) except OSError, e: if e.errno == errno.EINTR: continue if e.errno == errno.EPIPE or e.errno == errno.ECONNRESET: clean(idx, toread, fd) continue raise if x < len(chan["buf"][0]): chan["buf"][0] = chan["buf"][x:] else: del chan["buf"][0] if not chan["buf"] and chan["closed"]: chan["wr"].shutdown(socket.SHUT_WR) chan["buf"] = None # clean-up for chan in idx.values(): if chan["rd"] not in idx: # already deleted continue twin = idx[chan["wr"]] if not chan["closed"] or chan["buf"] or not twin["closed"] \ or twin["buf"]: continue clean(idx, toread, chan["rd"]) nemu-0.3.1/src/nemu/subprocess_.py000066400000000000000000000365301300327754000171230ustar00rootroot00000000000000# vim:ts=4:sw=4:et:ai:sts=4 # -*- coding: utf-8 -*- # Copyright 2010, 2011 INRIA # Copyright 2011 Martín Ferrari # # This file is part of Nemu. # # Nemu is free software: you can redistribute it and/or modify it under the # terms of the GNU General Public License version 2, as published by the Free # Software Foundation. # # Nemu is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR # A PARTICULAR PURPOSE. See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along with # Nemu. If not, see . import fcntl, grp, os, pickle, pwd, signal, select, sys, time, traceback from nemu.environ import eintr_wrapper __all__ = [ 'PIPE', 'STDOUT', 'Popen', 'Subprocess', 'spawn', 'wait', 'poll', 'get_user', 'system', 'backticks', 'backticks_raise' ] # User-facing interfaces KILL_WAIT = 3 # seconds class Subprocess(object): """Class that allows the execution of programs inside a nemu Node. This is the base class for all process operations, Popen provides a more high level interface.""" # FIXME default_user = None def __init__(self, node, argv, executable = None, stdin = None, stdout = None, stderr = None, shell = False, cwd = None, env = None, user = None): self._slave = node._slave """Forks and execs a program, with stdio redirection and user switching. A nemu Node to run the program is is specified as the first parameter. The program is specified by `executable', if it does not contain any slash, the PATH environment variable is used to search for the file. The `user` parameter, if not None, specifies a user name to run the command as, after setting its primary and secondary groups. If a numerical UID is given, a reverse lookup is performed to find the user name and then set correctly the groups. To run the program in a different directory than the current one, it should be set in `cwd'. If specified, `env' replaces the caller's environment with the dictionary provided. The standard input, output, and error of the created process will be redirected to the file descriptors specified by `stdin`, `stdout`, and `stderr`, respectively. These parameters must be open file objects, integers, or None (for no redirection). Note that the descriptors will not be closed by this class. Exceptions occurred while trying to set up the environment or executing the program are propagated to the parent.""" if user == None: user = Subprocess.default_user if isinstance(argv, str): argv = [ argv ] if shell: argv = [ '/bin/sh', '-c' ] + argv # Initialize attributes that would be used by the destructor if spawn # fails self._pid = self._returncode = None # confusingly enough, to go to the function at the top of this file, # I need to call it thru the communications protocol: remember that # happens in another process! self._pid = self._slave.spawn(argv, executable = executable, stdin = stdin, stdout = stdout, stderr = stderr, cwd = cwd, env = env, user = user) node._add_subprocess(self) @property def pid(self): """The real process ID of this subprocess.""" return self._pid def poll(self): """Checks status of program, returns exitcode or None if still running. See Popen.poll.""" if self._returncode == None: self._returncode = self._slave.poll(self._pid) return self.returncode def wait(self): """Waits for program to complete and returns the exitcode. See Popen.wait""" if self._returncode == None: self._returncode = self._slave.wait(self._pid) return self.returncode def signal(self, sig = signal.SIGTERM): """Sends a signal to the process.""" if self._returncode == None: self._slave.signal(self._pid, sig) @property def returncode(self): """When the program has finished (and has been waited for with communicate, wait, or poll), returns the signal that killed the program, if negative; otherwise, it is the exit code of the program. """ if self._returncode == None: return None if os.WIFSIGNALED(self._returncode): return -os.WTERMSIG(self._returncode) if os.WIFEXITED(self._returncode): return os.WEXITSTATUS(self._returncode) raise RuntimeError("Invalid return code") # pragma: no cover def __del__(self): self.destroy() def destroy(self): if self._returncode != None or self._pid == None: return self.signal() now = time.time() while time.time() - now < KILL_WAIT: if self.poll() != None: return time.sleep(0.1) sys.stderr.write("WARNING: killing forcefully process %d.\n" % self._pid) self.signal(signal.SIGKILL) self.wait() PIPE = -1 STDOUT = -2 class Popen(Subprocess): """Higher-level interface for executing processes, that tries to emulate the stdlib's subprocess.Popen as much as possible.""" def __init__(self, node, argv, executable = None, stdin = None, stdout = None, stderr = None, bufsize = 0, shell = False, cwd = None, env = None, user = None): """As in Subprocess, `node' specifies the nemu Node to run in. The `stdin', `stdout', and `stderr' parameters also accept the special values subprocess.PIPE or subprocess.STDOUT. Check the stdlib's subprocess module for more details. `bufsize' specifies the buffer size for the buffered IO provided for PIPE'd descriptors. """ self.stdin = self.stdout = self.stderr = None self._pid = self._returncode = None fdmap = { "stdin": stdin, "stdout": stdout, "stderr": stderr } # if PIPE: all should be closed at the end for k, v in fdmap.items(): if v == None: continue if v == PIPE: r, w = os.pipe() if k == "stdin": self.stdin = os.fdopen(w, 'wb', bufsize) fdmap[k] = r else: setattr(self, k, os.fdopen(r, 'rb', bufsize)) fdmap[k] = w elif isinstance(v, int): pass else: fdmap[k] = v.fileno() if stderr == STDOUT: fdmap['stderr'] = fdmap['stdout'] super(Popen, self).__init__(node, argv, executable = executable, stdin = fdmap['stdin'], stdout = fdmap['stdout'], stderr = fdmap['stderr'], shell = shell, cwd = cwd, env = env, user = user) # Close pipes, they have been dup()ed to the child for k, v in fdmap.items(): if getattr(self, k) != None: eintr_wrapper(os.close, v) def communicate(self, input = None): """See Popen.communicate.""" # FIXME: almost verbatim from stdlib version, need to be removed or # something wset = [] rset = [] err = None out = None if self.stdin != None: self.stdin.flush() if input: wset.append(self.stdin) else: self.stdin.close() if self.stdout != None: rset.append(self.stdout) out = [] if self.stderr != None: rset.append(self.stderr) err = [] offset = 0 while rset or wset: r, w, x = select.select(rset, wset, []) if self.stdin in w: wrote = os.write(self.stdin.fileno(), #buffer(input, offset, select.PIPE_BUF)) buffer(input, offset, 512)) # XXX: py2.7 offset += wrote if offset >= len(input): self.stdin.close() wset = [] for i in self.stdout, self.stderr: if i in r: d = os.read(i.fileno(), 1024) # No need for eintr wrapper if d == "": i.close rset.remove(i) else: if i == self.stdout: out.append(d) else: err.append(d) if out != None: out = ''.join(out) if err != None: err = ''.join(err) self.wait() return (out, err) def system(node, args): """Emulates system() function, if `args' is an string, it uses `/bin/sh' to exexecute it, otherwise is interpreted as the argv array to call execve.""" shell = isinstance(args, str) return Popen(node, args, shell = shell).wait() def backticks(node, args): """Emulates shell backticks, if `args' is an string, it uses `/bin/sh' to exexecute it, otherwise is interpreted as the argv array to call execve.""" shell = isinstance(args, str) return Popen(node, args, shell = shell, stdout = PIPE).communicate()[0] def backticks_raise(node, args): """Emulates shell backticks, if `args' is an string, it uses `/bin/sh' to exexecute it, otherwise is interpreted as the argv array to call execve. Raises an RuntimeError if the return value is not 0.""" shell = isinstance(args, str) p = Popen(node, args, shell = shell, stdout = PIPE) out = p.communicate()[0] ret = p.returncode if ret > 0: raise RuntimeError("Command failed with return code %d." % ret) if ret < 0: raise RuntimeError("Command killed by signal %d." % -ret) return out # ======================================================================= # # Server-side code, called from nemu.protocol.Server def spawn(executable, argv = None, cwd = None, env = None, close_fds = False, stdin = None, stdout = None, stderr = None, user = None): """Internal function that performs all the dirty work for Subprocess, Popen and friends. This is executed in the slave process, directly from the protocol.Server class. Parameters have the same meaning as the stdlib's subprocess.Popen class, with one addition: the `user` parameter, if not None, specifies a user name to run the command as, after setting its primary and secondary groups. If a numerical UID is given, a reverse lookup is performed to find the user name and then set correctly the groups. When close_fds is True, it closes all file descriptors bigger than 2. It can also be an iterable of file descriptors to close after fork. Note that 'std{in,out,err}' must be None, integers, or file objects, PIPE is not supported here. Also, the original descriptors are not closed. """ userfd = [stdin, stdout, stderr] filtered_userfd = filter(lambda x: x != None and x >= 0, userfd) for i in range(3): if userfd[i] != None and not isinstance(userfd[i], int): userfd[i] = userfd[i].fileno() # pragma: no cover # Verify there is no clash assert not (set([0, 1, 2]) & set(filtered_userfd)) if user != None: user, uid, gid = get_user(user) home = pwd.getpwuid(uid)[5] groups = [x[2] for x in grp.getgrall() if user in x[3]] if not env: env = dict(os.environ) env['HOME'] = home env['USER'] = user (r, w) = os.pipe() pid = os.fork() if pid == 0: # pragma: no cover # coverage doesn't seem to understand fork try: # Set up stdio piping for i in range(3): if userfd[i] != None and userfd[i] >= 0: os.dup2(userfd[i], i) if userfd[i] != i and userfd[i] not in userfd[0:i]: eintr_wrapper(os.close, userfd[i]) # only in child! # Set up special control pipe eintr_wrapper(os.close, r) flags = fcntl.fcntl(w, fcntl.F_GETFD) fcntl.fcntl(w, fcntl.F_SETFD, flags | fcntl.FD_CLOEXEC) if close_fds == True: for i in xrange(3, MAXFD): if i != w: try: os.close(i) except: pass elif close_fds != False: for i in close_fds: os.close(i) # changing process group id # (it is necessary to kill the forked subprocesses) os.setpgrp() if user != None: # Change user os.setgid(gid) os.setgroups(groups) os.setuid(uid) if cwd != None: os.chdir(cwd) if not argv: argv = [ executable ] if '/' in executable: # Should not search in PATH if env != None: os.execve(executable, argv, env) else: os.execv(executable, argv) else: # use PATH if env != None: os.execvpe(executable, argv, env) else: os.execvp(executable, argv) raise RuntimeError("Unreachable reached!") except: try: (t, v, tb) = sys.exc_info() # Got the child_traceback attribute trick from Python's # subprocess.py v.child_traceback = "".join( traceback.format_exception(t, v, tb)) eintr_wrapper(os.write, w, pickle.dumps(v)) eintr_wrapper(os.close, w) #traceback.print_exc() except: traceback.print_exc() os._exit(1) eintr_wrapper(os.close, w) # read EOF for success, or a string as error info s = "" while True: s1 = eintr_wrapper(os.read, r, 4096) if s1 == "": break s += s1 eintr_wrapper(os.close, r) if s == "": return pid # It was an error eintr_wrapper(os.waitpid, pid, 0) exc = pickle.loads(s) # XXX: sys.excepthook #print exc.child_traceback raise exc def poll(pid): """Check if the process already died. Returns the exit code or None if the process is still alive.""" r = os.waitpid(pid, os.WNOHANG) if r[0]: return r[1] return None def wait(pid): """Wait for process to die and return the exit code.""" return eintr_wrapper(os.waitpid, pid, 0)[1] def get_user(user): "Take either an username or an uid, and return a tuple (user, uid, gid)." if str(user).isdigit(): uid = int(user) try: user = pwd.getpwuid(uid)[0] except KeyError: raise ValueError("UID %d does not exist" % int(user)) else: try: uid = pwd.getpwnam(str(user))[2] except KeyError: raise ValueError("User %s does not exist" % str(user)) gid = pwd.getpwuid(uid)[3] return user, uid, gid # internal stuff, do not look! try: MAXFD = os.sysconf("SC_OPEN_MAX") except: # pragma: no cover MAXFD = 256 nemu-0.3.1/test/000077500000000000000000000000001300327754000134375ustar00rootroot00000000000000nemu-0.3.1/test/test_core.py000077500000000000000000000225241300327754000160100ustar00rootroot00000000000000#!/usr/bin/env python # vim:ts=4:sw=4:et:ai:sts=4 import grp, os, pwd, select, time, unittest import nemu, test_util class TestConfigure(unittest.TestCase): def test_config_run_as_static(self): # Don't allow root as default user self.assertRaises(AttributeError, setattr, nemu.config, 'run_as', 'root') self.assertRaises(AttributeError, setattr, nemu.config, 'run_as', 0) # Don't allow invalid users self.assertRaises(AttributeError, setattr, nemu.config, 'run_as', 'foobarbaz') # hope nobody has this user! self.assertRaises(AttributeError, setattr, nemu.config, 'run_as', 65536) try: pwd.getpwnam('nobody') nemu.config.run_as('nobody') self.assertEquals(nemu.config.run_as, 'nobody') except: pass class TestGlobal(unittest.TestCase): @test_util.skipUnless(os.getuid() == 0, "Test requires root privileges") def test_run_ping_p2pif(self): n1 = nemu.Node() n2 = nemu.Node() i1, i2 = nemu.P2PInterface.create_pair(n1, n2) i1.up = i2.up = True i1.lladdr = 'd6:4b:3f:f7:ff:7e' i2.lladdr = 'd6:4b:3f:f7:ff:7f' i1.add_v4_address('10.0.0.1', 24) i2.add_v4_address('10.0.0.2', 24) null = file('/dev/null', 'wb') a1 = n1.Popen(['ping', '-qc1', '10.0.0.2'], stdout = null) a2 = n2.Popen(['ping', '-qc1', '10.0.0.1'], stdout = null) self.assertEquals(a1.wait(), 0) self.assertEquals(a2.wait(), 0) # Test ipv6 autoconfigured addresses time.sleep(2) # Wait for autoconfiguration a1 = n1.Popen(['ping6', '-qc1', '-I', i1.name, 'fe80::d44b:3fff:fef7:ff7f'], stdout = null) a2 = n2.Popen(['ping6', '-qc1', '-I', i2.name, 'fe80::d44b:3fff:fef7:ff7e'], stdout = null) self.assertEquals(a1.wait(), 0) self.assertEquals(a2.wait(), 0) @test_util.skipUnless(os.getuid() == 0, "Test requires root privileges") def test_run_ping_node_if(self): n1 = nemu.Node() n2 = nemu.Node() i1 = n1.add_if() i2 = n2.add_if() i1.up = i2.up = True l = nemu.Switch() l.connect(i1) l.connect(i2) l.up = True i1.add_v4_address('10.0.0.1', 24) i2.add_v4_address('10.0.0.2', 24) null = file('/dev/null', 'wb') a1 = n1.Popen(['ping', '-qc1', '10.0.0.2'], stdout = null) a2 = n2.Popen(['ping', '-qc1', '10.0.0.1'], stdout = null) self.assertEquals(a1.wait(), 0) self.assertEquals(a2.wait(), 0) @test_util.skipUnless(os.getuid() == 0, "Test requires root privileges") def test_run_ping_routing_p2p(self): n1 = nemu.Node() n2 = nemu.Node() n3 = nemu.Node() i12, i21 = nemu.P2PInterface.create_pair(n1, n2) i23, i32 = nemu.P2PInterface.create_pair(n2, n3) i12.up = i21.up = i23.up = i32.up = True i12.add_v4_address('10.0.0.1', 24) i21.add_v4_address('10.0.0.2', 24) i23.add_v4_address('10.0.1.1', 24) i32.add_v4_address('10.0.1.2', 24) n1.add_route(prefix = '10.0.1.0', prefix_len = 24, nexthop = '10.0.0.2') n3.add_route(prefix = '10.0.0.0', prefix_len = 24, nexthop = '10.0.1.1') null = file('/dev/null', 'wb') a1 = n1.Popen(['ping', '-qc1', '10.0.1.2'], stdout = null) a2 = n3.Popen(['ping', '-qc1', '10.0.0.1'], stdout = null) self.assertEquals(a1.wait(), 0) self.assertEquals(a2.wait(), 0) @test_util.skipUnless(os.getuid() == 0, "Test requires root privileges") def test_run_ping_routing(self): n1 = nemu.Node() n2 = nemu.Node() n3 = nemu.Node() i1 = n1.add_if() i2a = n2.add_if() i2b = n2.add_if() i3 = n3.add_if() i1.up = i2a.up = i2b.up = i3.up = True l1 = nemu.Switch() l2 = nemu.Switch() l1.connect(i1) l1.connect(i2a) l2.connect(i2b) l2.connect(i3) l1.up = l2.up = True i1.add_v4_address('10.0.0.1', 24) i2a.add_v4_address('10.0.0.2', 24) i2b.add_v4_address('10.0.1.1', 24) i3.add_v4_address('10.0.1.2', 24) n1.add_route(prefix = '10.0.1.0', prefix_len = 24, nexthop = '10.0.0.2') n3.add_route(prefix = '10.0.0.0', prefix_len = 24, nexthop = '10.0.1.1') null = file('/dev/null', 'wb') a1 = n1.Popen(['ping', '-qc1', '10.0.1.2'], stdout = null) a2 = n3.Popen(['ping', '-qc1', '10.0.0.1'], stdout = null) self.assertEquals(a1.wait(), 0) self.assertEquals(a2.wait(), 0) def _forward_packets(self, subproc, if1, if2): while(True): ready = select.select([if1.fd, if2.fd], [], [], 0.1)[0] if ready: s = os.read(ready[0], 65536) if ready[0] == if1.fd: os.write(if2.fd, s) else: os.write(if1.fd, s) if not s: break if subproc.poll() != None: break @test_util.skipUnless(os.getuid() == 0, "Test requires root privileges") def test_run_ping_tun(self): """This test simulates a point to point connection between two hosts using two tun devices.""" n1 = nemu.Node() n2 = nemu.Node() # Use PI, so that's tested too. tun1 = n1.add_tun(use_pi = True) tun2 = n2.add_tun(use_pi = True) tun1.up = tun2.up = True tun1.add_v4_address('10.0.1.1', 24) tun2.add_v4_address('10.0.1.2', 24) null = file('/dev/null', 'wb') a = n1.Popen(['ping', '-qc1', '10.0.1.2'], stdout = null) self._forward_packets(a, tun1, tun2) self.assertEquals(a.wait(), 0) @test_util.skipUnless(os.getuid() == 0, "Test requires root privileges") def test_run_ping_tap(self): """This test simulates a point to point connection between two hosts using two tap devices.""" n1 = nemu.Node() n2 = nemu.Node() tap1 = n1.add_tap() tap2 = n2.add_tap() tap1.up = tap2.up = True tap1.add_v4_address('10.0.1.1', 24) tap2.add_v4_address('10.0.1.2', 24) null = file('/dev/null', 'wb') a = n1.Popen(['ping', '-qc1', '10.0.1.2'], stdout = null) self._forward_packets(a, tap1, tap2) self.assertEquals(a.wait(), 0) @test_util.skipUnless(os.getuid() == 0, "Test requires root privileges") def test_run_ping_tap_routing(self): """This test simulates a point to point connection between two hosts using two tap devices, and normal connections with other two, to use routing.""" n1 = nemu.Node() n2 = nemu.Node() n3 = nemu.Node() n4 = nemu.Node() i1 = n1.add_if() i2 = n2.add_if() tap1 = n2.add_tap() tap2 = n3.add_tap() i3 = n3.add_if() i4 = n4.add_if() i1.up = i2.up = tap1.up = tap2.up = i3.up = i4.up = True l1 = nemu.Switch() l2 = nemu.Switch() l1.connect(i1) l1.connect(i2) l2.connect(i3) l2.connect(i4) l1.up = l2.up = True i1.add_v4_address('10.0.0.1', 24) i2.add_v4_address('10.0.0.2', 24) tap1.add_v4_address('10.0.1.1', 24) tap2.add_v4_address('10.0.1.2', 24) i3.add_v4_address('10.0.2.1', 24) i4.add_v4_address('10.0.2.2', 24) n1.add_route(prefix = '10.0.1.0', prefix_len = 24, nexthop = '10.0.0.2') n1.add_route(prefix = '10.0.2.0', prefix_len = 24, nexthop = '10.0.0.2') n2.add_route(prefix = '10.0.2.0', prefix_len = 24, nexthop = '10.0.1.2') n3.add_route(prefix = '10.0.0.0', prefix_len = 24, nexthop = '10.0.1.1') n4.add_route(prefix = '10.0.1.0', prefix_len = 24, nexthop = '10.0.2.1') n4.add_route(prefix = '10.0.0.0', prefix_len = 24, nexthop = '10.0.2.1') null = file('/dev/null', 'wb') a = n1.Popen(['ping', '-qc1', '10.0.2.2'], stdout = null) self._forward_packets(a, tap1, tap2) self.assertEquals(a.wait(), 0) class TestX11(unittest.TestCase): @test_util.skipUnless("DISPLAY" in os.environ, "Test requires working X11") @test_util.skipUnless(nemu.environ.XDPYINFO_PATH, "Test requires xdpyinfo") def test_run_xdpyinfo(self): xdpy = nemu.environ.XDPYINFO_PATH info = nemu.environ.backticks([xdpy]) # remove first line, contains the display name info = info.partition("\n")[2] n = nemu.Node(nonetns = True, forward_X11 = True) info2 = n.backticks([xdpy]) info2 = info2.partition("\n")[2] self.assertEquals(info, info2) @test_util.skipUnless(os.getuid() == 0, "Test requires root privileges") @test_util.skipUnless("DISPLAY" in os.environ, "Test requires working X11") @test_util.skipUnless(nemu.environ.XDPYINFO_PATH, "Test requires xdpyinfo") def test_run_xdpyinfo_netns(self): xdpy = nemu.environ.XDPYINFO_PATH info = nemu.environ.backticks([xdpy]) # remove first line, contains the display name info = info.partition("\n")[2] n = nemu.Node(forward_X11 = True) info2 = n.backticks([xdpy]) info2 = info2.partition("\n")[2] self.assertEquals(info, info2) if __name__ == '__main__': unittest.main() nemu-0.3.1/test/test_interfaces.py000077500000000000000000000211641300327754000172020ustar00rootroot00000000000000#!/usr/bin/env python # vim:ts=4:sw=4:et:ai:sts=4 from test_util import get_devs, get_devs_netns from nemu.environ import * import nemu, test_util import os, unittest class TestUtils(unittest.TestCase): def test_utils(self): devs = get_devs() # There should be at least loopback! self.assertTrue(len(devs) > 0) self.assertTrue('lo' in devs) self.assertTrue(devs['lo']['up']) self.assertEquals(devs['lo']['lladdr'], '00:00:00:00:00:00') self.assertTrue( { 'address': '127.0.0.1', 'prefix_len': 8, 'broadcast': None, 'family': 'inet' } in devs['lo']['addr']) class TestIPRouteStuff(unittest.TestCase): def test_fix_lladdr(self): fl = nemu.iproute._fix_lladdr self.assertEquals(fl('42:71:e0:90:ca:42'), '42:71:e0:90:ca:42') self.assertEquals(fl('4271E090CA42'), '42:71:e0:90:ca:42', 'Normalization of link-level address: missing colons and ' 'upper caps') self.assertEquals(fl('2:71:E:90:CA:42'), '02:71:0e:90:ca:42', 'Normalization of link-level address: missing zeroes') self.assertEquals(fl('271E090CA42'), '02:71:e0:90:ca:42', 'Automatic normalization of link-level address: missing ' 'colons and zeroes') self.assertRaises(ValueError, fl, 'foo') self.assertRaises(ValueError, fl, '42:71:e0:90:ca42') self.assertRaises(ValueError, fl, '1234567890123') def test_any_to_bool(self): a2b = nemu.iproute._any_to_bool for i in (True, 2, 'trUe', '1', 'foo', 1.0, [1]): self.assertTrue(a2b(i)) for i in (False, 0, 'falsE', '0', '', 0.0, []): self.assertFalse(a2b(i)) def test_non_empty_str(self): nes = nemu.iproute._non_empty_str self.assertEquals(nes(''), None) self.assertEquals(nes('Foo'), 'Foo') self.assertEquals(nes(1), '1') def test_interface(self): i = nemu.iproute.interface(index = 1) self.assertRaises(AttributeError, setattr, i, 'index', 2) self.assertRaises(ValueError, setattr, i, 'mtu', -1) self.assertEquals(repr(i), 'nemu.iproute.interface(index = 1, ' 'name = None, up = None, mtu = None, lladdr = None, ' 'broadcast = None, multicast = None, arp = None)') i.name = 'foo'; i.up = 1; i.arp = True; i.mtu = 1500 self.assertEquals(repr(i), 'nemu.iproute.interface(index = 1, ' 'name = \'foo\', up = True, mtu = 1500, lladdr = None, ' 'broadcast = None, multicast = None, arp = True)') j = nemu.iproute.interface(index = 2) j.name = 'bar'; j.up = False; j.arp = 1 # Modifications to turn j into i. self.assertEquals(repr(i - j), 'nemu.iproute.interface(index = 1, ' 'name = \'foo\', up = True, mtu = 1500, lladdr = None, ' 'broadcast = None, multicast = None, arp = None)') # Modifications to turn i into j. self.assertEquals(repr(j - i), 'nemu.iproute.interface(index = 2, ' 'name = \'bar\', up = False, mtu = None, lladdr = None, ' 'broadcast = None, multicast = None, arp = None)') class TestInterfaces(unittest.TestCase): @test_util.skipUnless(os.getuid() == 0, "Test requires root privileges") def test_interface_creation(self): node0 = nemu.Node() ifaces = [] for i in range(5): ifaces.append(node0.add_if()) devs = get_devs_netns(node0) for i in range(5): self.assertTrue(devs['lo']['up']) self.assertTrue(ifaces[i].name in devs) node_devs = set(node0.get_interfaces()) self.assertTrue(set(ifaces).issubset(node_devs)) loopback = node_devs - set(ifaces) # should be! self.assertEquals(len(loopback), 1) self.assertEquals(loopback.pop().name, 'lo') devs = get_devs() for i in range(5): peer_name = nemu.iproute.get_if(ifaces[i].control.index).name self.assertTrue(peer_name in devs) @test_util.skipUnless(os.getuid() == 0, "Test requires root privileges") def test_interface_settings(self): node0 = nemu.Node() if0 = node0.add_if(lladdr = '42:71:e0:90:ca:42', mtu = 1492) self.assertEquals(if0.lladdr, '42:71:e0:90:ca:42', "Constructor parameters") self.assertEquals(if0.mtu, 1492, "Constructor parameters") if0.lladdr = '4271E090CA42' self.assertEquals(if0.lladdr, '42:71:e0:90:ca:42', """Normalization of link-level address: missing colons and upper caps""") if0.lladdr = '2:71:E0:90:CA:42' self.assertEquals(if0.lladdr, '02:71:e0:90:ca:42', """Normalization of link-level address: missing zeroes""") if0.lladdr = '271E090CA42' self.assertEquals(if0.lladdr, '02:71:e0:90:ca:42', """Automatic normalization of link-level address: missing colons and zeroes""") self.assertRaises(ValueError, setattr, if0, 'lladdr', 'foo') self.assertRaises(ValueError, setattr, if0, 'lladdr', '1234567890123') self.assertEquals(if0.mtu, 1492) # detected by setter self.assertRaises(ValueError, setattr, if0, 'mtu', 0) # error from ip self.assertRaises(RuntimeError, setattr, if0, 'mtu', 1) self.assertRaises(RuntimeError, setattr, if0, 'mtu', 65537) devs = get_devs_netns(node0) self.assertTrue(if0.name in devs) self.assertFalse(devs[if0.name]['up']) self.assertEquals(devs[if0.name]['lladdr'], if0.lladdr) self.assertEquals(devs[if0.name]['mtu'], if0.mtu) if0.up = True devs = get_devs_netns(node0) self.assertTrue(devs[if0.name]['up']) # Verify that data is actually read from the kernel r = node0.system([IP_PATH, "link", "set", if0.name, "mtu", "1500"]) self.assertEquals(r, 0) devs = get_devs_netns(node0) self.assertEquals(devs[if0.name]['mtu'], 1500) self.assertEquals(devs[if0.name]['mtu'], if0.mtu) # FIXME: get_stats @test_util.skipUnless(os.getuid() == 0, "Test requires root privileges") def test_interface_addresses(self): node0 = nemu.Node() if0 = node0.add_if() if0.add_v4_address(address = '10.0.0.1', prefix_len = 24, broadcast = '10.0.0.255') if0.add_v4_address(address = '10.0.2.1', prefix_len = 26) if0.add_v6_address(address = 'fe80::222:19ff:fe22:615d', prefix_len = 64) devs = get_devs_netns(node0) self.assertTrue( { 'address': '10.0.0.1', 'prefix_len': 24, 'broadcast': '10.0.0.255', 'family': 'inet' } in devs[if0.name]['addr']) self.assertTrue( { 'address': '10.0.2.1', 'prefix_len': 26, 'broadcast': '10.0.2.63', 'family': 'inet' } in devs[if0.name]['addr']) self.assertTrue( { 'address': 'fe80::222:19ff:fe22:615d', 'prefix_len': 64, 'family': 'inet6' } in devs[if0.name]['addr']) self.assertTrue(len(if0.get_addresses()) >= 2) self.assertEquals(if0.get_addresses(), devs[if0.name]['addr']) class TestWithDummy(unittest.TestCase): def setUp(self): self.cleanup = [] @test_util.skipUnless(os.getuid() == 0, "Test requires root privileges") @test_util.skipUnless( test_util.get_linux_ver() >= test_util.make_linux_ver("2.6.35"), "Test trigger a kernel bug on 2.6.34") def test_interface_migration(self): node = nemu.Node() self.dummyname = "dummy%d" % os.getpid() self.assertEquals(os.system("%s link add name %s type dummy" % (IP_PATH, self.dummyname)), 0) devs = get_devs() self.assertTrue(self.dummyname in devs) dummyidx = devs[self.dummyname]['idx'] if0 = node.import_if(self.dummyname) self.assertTrue(self.dummyname not in get_devs()) node_devs = dict([(i.index, i) for i in node.get_interfaces()]) self.assertTrue(dummyidx in node_devs) if0.lladdr = '42:71:e0:90:ca:43' if0.mtu = 1400 devs = get_devs_netns(node) self.assertTrue(if0.name in devs) self.assertEquals(devs[if0.name]['lladdr'], '42:71:e0:90:ca:43') self.assertEquals(devs[if0.name]['mtu'], 1400) node.destroy() self.assertTrue(self.dummyname in get_devs()) def tearDown(self): # oops here if hasattr(self, 'dummyname'): os.system("%s link del %s" % (IP_PATH, self.dummyname)) # FIXME: Links if __name__ == '__main__': unittest.main() nemu-0.3.1/test/test_node.py000077500000000000000000000045771300327754000160150ustar00rootroot00000000000000#!/usr/bin/env python # vim:ts=4:sw=4:et:ai:sts=4 import nemu, nemu.environ, test_util import os, signal, subprocess, sys, time import unittest class TestNode(unittest.TestCase): @test_util.skipUnless(os.getuid() == 0, "Test requires root privileges") def test_node(self): node = nemu.Node() self.failIfEqual(node.pid, os.getpid()) self.failIfEqual(node.pid, None) # check if it really exists os.kill(node.pid, 0) nodes = nemu.get_nodes() self.assertEquals(nodes, [node]) self.assertTrue(node.get_interface("lo").up) @test_util.skip("Not implemented") def test_detect_fork(self): # Test that nemu recognises a fork chld = os.fork() if chld == 0: if len(nemu.get_nodes()) == 0: os._exit(0) os._exit(1) (pid, exitcode) = os.waitpid(chld, 0) self.assertEquals(exitcode, 0, "Node does not recognise forks") @test_util.skipUnless(os.getuid() == 0, "Test requires root privileges") def test_cleanup(self): def create_stuff(): a = nemu.Node() b = nemu.Node() ifa = a.add_if() ifb = b.add_if() switch = nemu.Switch() switch.connect(ifa) switch.connect(ifb) # Test automatic destruction orig_devs = len(test_util.get_devs()) create_stuff() self.assertEquals(nemu.get_nodes(), []) self.assertEquals(orig_devs, len(test_util.get_devs())) # Test at_exit hooks orig_devs = len(test_util.get_devs()) chld = os.fork() if chld == 0: # TODO: not implemented. #nemu.set_cleanup_hooks(on_exit = True, on_signals = []) create_stuff() os._exit(0) os.waitpid(chld, 0) self.assertEquals(orig_devs, len(test_util.get_devs())) # Test signal hooks orig_devs = len(test_util.get_devs()) chld = os.fork() if chld == 0: # TODO: not implemented. #nemu.set_cleanup_hooks(on_exit = False, # on_signals = [signal.SIGTERM]) create_stuff() while True: time.sleep(10) os.kill(chld, signal.SIGTERM) os.waitpid(chld, 0) self.assertEquals(orig_devs, len(test_util.get_devs())) if __name__ == '__main__': unittest.main() nemu-0.3.1/test/test_protocol.py000077500000000000000000000124201300327754000167130ustar00rootroot00000000000000#!/usr/bin/env python # vim:ts=4:sw=4:et:ai:sts=4 import nemu.protocol import os, socket, sys, threading, unittest class TestServer(unittest.TestCase): def test_server_startup(self): # Test the creation of the server object with different ways of passing # the file descriptor; and check the banner. (s0, s1) = socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM, 0) (s2, s3) = socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM, 0) def test_help(fd): fd.write("HELP\n") # should be more than one line self.assertEquals(fd.readline()[0:4], "200-") while True: l = fd.readline() self.assertEquals(l[0:3], "200") if l[3] == ' ': break def run_server(): srv = nemu.protocol.Server(s0, s0) srv.run() srv = nemu.protocol.Server(s2.fileno(), s2.fileno()) srv.run() t = threading.Thread(target = run_server) t.start() s = os.fdopen(s1.fileno(), "r+", 1) self.assertEquals(s.readline()[0:4], "220 ") test_help(s) s.close() s0.close() s = os.fdopen(s3.fileno(), "r+", 1) self.assertEquals(s.readline()[0:4], "220 ") test_help(s) s.close() s2.close() t.join() def test_server_clean(self): (s0, s1) = socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM, 0) def run_server(): nemu.protocol.Server(s0, s0).run() t = threading.Thread(target = run_server) t.start() cli = nemu.protocol.Client(s1, s1) null = file('/dev/null', 'wb') argv = [ '/bin/sh', '-c', 'yes' ] pid = cli.spawn(argv, stdout = null) self.assertTrue(os.path.exists("/proc/%d" % pid)) # try to exit while there are still processes running cli.shutdown() t.join() # Check that the process was killed. # We are asumming that the pid is not going to be reused fast enough # to generate a false possitive. self.assertFalse(os.path.exists("/proc/%d" % pid)) def test_spawn_recovery(self): (s0, s1) = socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM, 0) def run_server(): nemu.protocol.Server(s0, s0).run() t = threading.Thread(target = run_server) t.start() cli = nemu.protocol.Client(s1, s1) # make PROC SIN fail self.assertRaises(OSError, cli.spawn, "/bin/true", stdin = -1) # check if the protocol is in a sane state: # PROC CWD should not be valid cli._send_cmd("PROC", "CWD", "/") self.assertRaises(RuntimeError, cli._read_and_check_reply) # Force a KeyError, and check that the exception is received correctly cli._send_cmd("IF", "LIST", "-1") self.assertRaises(KeyError, cli._read_and_check_reply) cli.shutdown() t.join() def test_basic_stuff(self): (s0, s1) = socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM, 0) srv = nemu.protocol.Server(s0, s0) s1 = s1.makefile("r+", 1) def check_error(self, cmd, code = 500): s1.write("%s\n" % cmd) self.assertEquals(srv.readcmd(), None) self.assertEquals(s1.readline()[0:4], "%d " % code) def check_ok(self, cmd, func, args): s1.write("%s\n" % cmd) ccmd = " ".join(cmd.upper().split()[0:2]) if func == None: self.assertEquals(srv.readcmd()[1:3], (ccmd, args)) else: self.assertEquals(srv.readcmd(), (func, ccmd, args)) check_ok(self, "quit", srv.do_QUIT, []) check_ok(self, " quit ", srv.do_QUIT, []) # protocol error check_error(self, "quit 1") # Not allowed in normal mode check_error(self, "proc user") check_error(self, "proc sin") check_error(self, "proc sout") check_error(self, "proc serr") check_error(self, "proc cwd") check_error(self, "proc env") check_error(self, "proc abrt") check_error(self, "proc run") check_ok(self, "if list", srv.do_IF_LIST, []) check_ok(self, "if list 1", srv.do_IF_LIST, [1]) check_error(self, "proc poll") # missing arg check_error(self, "proc poll 1 2") # too many args check_error(self, "proc poll a") # invalid type check_error(self, "proc") # incomplete command check_error(self, "proc foo") # unknown subcommand check_error(self, "foo bar") # unknown check_ok(self, "proc crte /bin/sh", srv.do_PROC_CRTE, ['/bin/sh']) # Commands that would fail, but the parsing is correct check_ok(self, "proc poll 0", None, [0]) check_ok(self, "proc wait 0", None, [0]) check_ok(self, "proc kill 0", None, [0]) check_ok(self, "proc crte =", srv.do_PROC_CRTE, [""]) # empty b64 check_error(self, "proc crte =a") # invalid b64 # simulate proc mode srv._commands = nemu.protocol._proc_commands check_error(self, "proc crte foo") check_error(self, "proc poll 0") check_error(self, "proc wait 0") check_error(self, "proc kill 0") if __name__ == '__main__': unittest.main() nemu-0.3.1/test/test_routing.py000077500000000000000000000040041300327754000165400ustar00rootroot00000000000000#!/usr/bin/env python # vim:ts=4:sw=4:et:ai:sts=4 import nemu, test_util import os, unittest class TestRouting(unittest.TestCase): @test_util.skip("Programatic detection of duplicate routes not implemented") def test_base_routing(self): node = nemu.Node(nonetns = True) routes = node.get_routes() # main netns routes! if(len(routes)): self.assertRaises(RuntimeError, node.add_route, routes[0]) routes[0].metric += 1 # should be enough to make it unique self.assertRaises(RuntimeError, node.del_route, routes[0]) @test_util.skipUnless(os.getuid() == 0, "Test requires root privileges") def test_routing(self): node = nemu.Node() self.assertEquals(len(node.get_routes()), 0) if0 = node.add_if() if0.add_v4_address('10.0.0.1', 24) if0.up = True routes = node.get_routes() self.assertEquals(routes, [node.route(prefix = '10.0.0.0', prefix_len = 24, interface = if0)]) node.add_route(nexthop = '10.0.0.2') # default route node.add_route(prefix = '10.1.0.0', prefix_len = 16, nexthop = '10.0.0.3') node.add_route(prefix = '11.1.0.1', prefix_len = 32, interface = if0) routes = node.get_routes() self.assertTrue(node.route(nexthop = '10.0.0.2', interface = if0) in routes) self.assertTrue(node.route(prefix = '10.1.0.0', prefix_len = 16, nexthop = '10.0.0.3', interface = if0) in routes) self.assertTrue(node.route(prefix = '11.1.0.1', prefix_len = 32, interface = if0) in routes) node.del_route(nexthop = '10.0.0.2') # default route node.del_route(prefix = '10.1.0.0', prefix_len = 16, nexthop = '10.0.0.3') node.del_route(prefix = '11.1.0.1', prefix_len = 32, interface = if0) node.del_route(prefix = '10.0.0.0', prefix_len = 24, interface = if0) self.assertEquals(node.get_routes(), []) if __name__ == '__main__': unittest.main() nemu-0.3.1/test/test_subprocess.py000077500000000000000000000271241300327754000172510ustar00rootroot00000000000000#!/usr/bin/env python # vim:ts=4:sw=4:et:ai:sts=4 import nemu, test_util import nemu.subprocess_ as sp import grp, os, pwd, signal, socket, sys, time, unittest def _stat(path): try: return os.stat(user) except: return None def _getpwnam(user): try: return pwd.getpwnam(user) except: return None def _getpwuid(uid): try: return pwd.getpwuid(uid) except: return None def _readall(fd): s = "" while True: try: s1 = os.read(fd, 4096) except OSError, e: if e.errno == errno.EINTR: continue else: raise if s1 == "": break s += s1 return s _longstring = "Long string is long!\n" * 1000 class TestSubprocess(unittest.TestCase): def _check_ownership(self, user, pid): uid = pwd.getpwnam(user)[2] gid = pwd.getpwnam(user)[3] groups = [x[2] for x in grp.getgrall() if user in x[3]] stat = open("/proc/%d/status" % pid) while True: data = stat.readline() fields = data.split() if fields[0] == 'Uid:': self.assertEquals(fields[1:4], (uid,) * 4) if fields[0] == 'Gid:': self.assertEquals(fields[1:4], (gid,) * 4) if fields[0] == 'Groups:': self.assertEquals(set(fields[1:]), set(groups)) break stat.close() def setUp(self): self.nouid = 65535 while _getpwuid(self.nouid): self.nouid -= 1 self.nouser = 'foobar' while _getpwnam(self.nouser): self.nouser += '_' self.nofile = '/foobar' while _stat(self.nofile): self.nofile += '_' @test_util.skipUnless(os.getuid() == 0, "Test requires root privileges") def test_spawn_chuser(self): user = 'nobody' pid = sp.spawn('/bin/sleep', ['/bin/sleep', '100'], user = user) self._check_ownership(user, pid) os.kill(pid, signal.SIGTERM) self.assertEquals(sp.wait(pid), signal.SIGTERM) @test_util.skipUnless(os.getuid() == 0, "Test requires root privileges") def test_Subprocess_chuser(self): node = nemu.Node(nonetns = True) user = 'nobody' p = node.Subprocess(['/bin/sleep', '1000'], user = user) self._check_ownership(user, p.pid) p.signal() self.assertEquals(p.wait(), -signal.SIGTERM) def test_spawn_basic(self): # User does not exist self.assertRaises(ValueError, sp.spawn, '/bin/sleep', ['/bin/sleep', '1000'], user = self.nouser) self.assertRaises(ValueError, sp.spawn, '/bin/sleep', ['/bin/sleep', '1000'], user = self.nouid) # Invalid CWD: it is a file self.assertRaises(OSError, sp.spawn, '/bin/sleep', cwd = '/bin/sleep') # Invalid CWD: does not exist self.assertRaises(OSError, sp.spawn, '/bin/sleep', cwd = self.nofile) # Exec failure self.assertRaises(OSError, sp.spawn, self.nofile) # Test that the environment is cleared: sleep should not be found # XXX: This should be a python bug: if I don't set PATH explicitly, it # uses a default search path self.assertRaises(OSError, sp.spawn, 'sleep', env = {'PATH': ''}) r, w = os.pipe() p = sp.spawn('/bin/echo', ['echo', 'hello world'], stdout = w) os.close(w) self.assertEquals(_readall(r), "hello world\n") os.close(r) # Check poll. while True: ret = sp.poll(p) if ret is not None: self.assertEquals(ret, 0) break time.sleep(0.2) # Wait a little bit. # It cannot be wait()ed again. self.assertRaises(OSError, sp.wait, p) r0, w0 = os.pipe() r1, w1 = os.pipe() p = sp.spawn('/bin/cat', stdout = w0, stdin = r1, close_fds = [r0, w1]) os.close(w0) os.close(r1) self.assertEquals(sp.poll(p), None) os.write(w1, "hello world\n") os.close(w1) self.assertEquals(_readall(r0), "hello world\n") os.close(r0) self.assertEquals(sp.wait(p), 0) def test_Subprocess_basic(self): node = nemu.Node(nonetns = True) # User does not exist self.assertRaises(ValueError, node.Subprocess, ['/bin/sleep', '1000'], user = self.nouser) self.assertRaises(ValueError, node.Subprocess, ['/bin/sleep', '1000'], user = self.nouid) # Invalid CWD: it is a file self.assertRaises(OSError, node.Subprocess, '/bin/sleep', cwd = '/bin/sleep') # Invalid CWD: does not exist self.assertRaises(OSError, node.Subprocess, '/bin/sleep', cwd = self.nofile) # Exec failure self.assertRaises(OSError, node.Subprocess, self.nofile) # Test that the environment is cleared: sleep should not be found self.assertRaises(OSError, node.Subprocess, 'sleep', env = {'PATH': ''}) # Argv self.assertRaises(OSError, node.Subprocess, 'true; false') self.assertEquals(node.Subprocess('true').wait(), 0) self.assertEquals(node.Subprocess('true; false', shell = True).wait(), 1) # Piping r, w = os.pipe() p = node.Subprocess(['echo', 'hello world'], stdout = w) os.close(w) self.assertEquals(_readall(r), "hello world\n") os.close(r) p.wait() # cwd r, w = os.pipe() p = node.Subprocess('/bin/pwd', stdout = w, cwd = "/") os.close(w) self.assertEquals(_readall(r), "/\n") os.close(r) p.wait() p = node.Subprocess(['sleep', '100']) self.assertTrue(p.pid > 0) self.assertEquals(p.poll(), None) # not finished p.signal() p.signal() # verify no-op (otherwise there will be an exception) self.assertEquals(p.wait(), -signal.SIGTERM) self.assertEquals(p.wait(), -signal.SIGTERM) # no-op self.assertEquals(p.poll(), -signal.SIGTERM) # no-op # destroy p = node.Subprocess(['sleep', '100']) pid = p.pid os.kill(pid, 0) # verify process still there p.destroy() self.assertRaises(OSError, os.kill, pid, 0) # should be dead by now # forceful destroy # Command: ignore SIGTERM, write \n to synchronise and then sleep while # closing stdout (so _readall finishes) cmd = 'trap "" TERM; echo; exec sleep 100 > /dev/null' r, w = os.pipe() p = node.Subprocess(cmd, shell = True, stdout = w) os.close(w) self.assertEquals(_readall(r), "\n") # wait for trap to be installed os.close(r) pid = p.pid os.kill(pid, 0) # verify process still there # Avoid the warning about the process being killed orig_stderr = sys.stderr sys.stderr = open("/dev/null", "w") p.destroy() sys.stderr = orig_stderr self.assertRaises(OSError, os.kill, pid, 0) # should be dead by now p = node.Subprocess(['sleep', '100']) os.kill(p.pid, signal.SIGTERM) time.sleep(0.2) p.signal() # since it has not been waited for, it should not raise self.assertEquals(p.wait(), -signal.SIGTERM) def test_Popen(self): node = nemu.Node(nonetns = True) # repeat test with Popen interface r0, w0 = os.pipe() r1, w1 = os.pipe() p = node.Popen('cat', stdout = w0, stdin = r1) os.close(w0) os.close(r1) os.write(w1, "hello world\n") os.close(w1) self.assertEquals(_readall(r0), "hello world\n") os.close(r0) # now with a socketpair, not using integers (s0, s1) = socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM, 0) p = node.Popen('cat', stdout = s0, stdin = s0) s0.close() s1.send("hello world\n") self.assertEquals(s1.recv(512), "hello world\n") s1.close() # pipes p = node.Popen('cat', stdin = sp.PIPE, stdout = sp.PIPE) p.stdin.write("hello world\n") p.stdin.close() self.assertEquals(p.stdout.readlines(), ["hello world\n"]) self.assertEquals(p.stderr, None) self.assertEquals(p.wait(), 0) p = node.Popen('cat', stdin = sp.PIPE, stdout = sp.PIPE) self.assertEquals(p.communicate(_longstring), (_longstring, None)) p = node.Popen('cat', stdin = sp.PIPE, stdout = sp.PIPE) p.stdin.write(_longstring) self.assertEquals(p.communicate(), (_longstring, None)) p = node.Popen('cat', stdin = sp.PIPE) self.assertEquals(p.communicate(), (None, None)) p = node.Popen('cat >&2', shell = True, stdin = sp.PIPE, stderr = sp.PIPE) p.stdin.write("hello world\n") p.stdin.close() self.assertEquals(p.stderr.readlines(), ["hello world\n"]) self.assertEquals(p.stdout, None) self.assertEquals(p.wait(), 0) p = node.Popen(['sh', '-c', 'cat >&2'], stdin = sp.PIPE, stderr = sp.PIPE) self.assertEquals(p.communicate(_longstring), (None, _longstring)) # p = node.Popen(['sh', '-c', 'cat >&2'], stdin = sp.PIPE, stdout = sp.PIPE, stderr = sp.STDOUT) p.stdin.write("hello world\n") p.stdin.close() self.assertEquals(p.stdout.readlines(), ["hello world\n"]) self.assertEquals(p.stderr, None) self.assertEquals(p.wait(), 0) p = node.Popen(['sh', '-c', 'cat >&2'], stdin = sp.PIPE, stdout = sp.PIPE, stderr = sp.STDOUT) self.assertEquals(p.communicate(_longstring), (_longstring, None)) # p = node.Popen(['tee', '/dev/stderr'], stdin = sp.PIPE, stdout = sp.PIPE, stderr = sp.STDOUT) p.stdin.write("hello world\n") p.stdin.close() self.assertEquals(p.stdout.readlines(), ["hello world\n"] * 2) self.assertEquals(p.stderr, None) self.assertEquals(p.wait(), 0) p = node.Popen(['tee', '/dev/stderr'], stdin = sp.PIPE, stdout = sp.PIPE, stderr = sp.STDOUT) self.assertEquals(p.communicate(_longstring[0:512]), (_longstring[0:512] * 2, None)) # p = node.Popen(['tee', '/dev/stderr'], stdin = sp.PIPE, stdout = sp.PIPE, stderr = sp.PIPE) p.stdin.write("hello world\n") p.stdin.close() self.assertEquals(p.stdout.readlines(), ["hello world\n"]) self.assertEquals(p.stderr.readlines(), ["hello world\n"]) self.assertEquals(p.wait(), 0) p = node.Popen(['tee', '/dev/stderr'], stdin = sp.PIPE, stdout = sp.PIPE, stderr = sp.PIPE) self.assertEquals(p.communicate(_longstring), (_longstring, ) * 2) def test_backticks(self): node = nemu.Node(nonetns = True) self.assertEquals(node.backticks("echo hello world"), "hello world\n") self.assertEquals(node.backticks(r"echo hello\ \ world"), "hello world\n") self.assertEquals(node.backticks(["echo", "hello", "world"]), "hello world\n") self.assertEquals(node.backticks("echo hello world > /dev/null"), "") self.assertEquals(node.backticks_raise("true"), "") self.assertRaises(RuntimeError, node.backticks_raise, "false") self.assertRaises(RuntimeError, node.backticks_raise, "kill $$") def test_system(self): node = nemu.Node(nonetns = True) self.assertEquals(node.system("true"), 0) self.assertEquals(node.system("false"), 1) if __name__ == '__main__': unittest.main() nemu-0.3.1/test/test_switch.py000077500000000000000000000106741300327754000163640ustar00rootroot00000000000000#!/usr/bin/env python # vim:ts=4:sw=4:et:ai:sts=4 import os, unittest import nemu, test_util, nemu.environ class TestSwitch(unittest.TestCase): @test_util.skipUnless(os.getuid() == 0, "Test requires root privileges") def setUp(self): n1 = nemu.Node() n2 = nemu.Node() i1 = n1.add_if() i2 = n2.add_if() l = nemu.Switch() l.connect(i1) l.connect(i2) self.stuff = (n1, n2, i1, i2, l) @test_util.skipUnless(os.getuid() == 0, "Test requires root privileges") def test_switch_base(self): (n1, n2, i1, i2, l) = self.stuff l.mtu = 3000 ifdata = nemu.iproute.get_if_data()[0] self.assertEquals(ifdata[l.index].mtu, 3000) self.assertEquals(ifdata[i1.control.index].mtu, 3000, "MTU propagation") self.assertEquals(ifdata[i2.control.index].mtu, 3000, "MTU propagation") i1.mtu = i2.mtu = 3000 self.assertEquals(ifdata[l.index].up, False) self.assertEquals(ifdata[i1.control.index].up, False, "UP propagation") self.assertEquals(ifdata[i2.control.index].up, False, "UP propagation") l.up = True ifdata = nemu.iproute.get_if_data()[0] self.assertEquals(ifdata[i1.control.index].up, True, "UP propagation") self.assertEquals(ifdata[i2.control.index].up, True, "UP propagation") tcdata = nemu.iproute.get_tc_data()[0] self.assertEquals(tcdata[i1.control.index], {"qdiscs": {}}) self.assertEquals(tcdata[i2.control.index], {"qdiscs": {}}) @test_util.skipUnless(os.getuid() == 0, "Test requires root privileges") def test_switch_changes(self): (n1, n2, i1, i2, l) = self.stuff # Test strange rules handling os.system(("%s qd add dev %s root prio bands 3 " + "priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1") % (nemu.environ.TC_PATH, i1.control.name)) tcdata = nemu.iproute.get_tc_data()[0] self.assertEquals(tcdata[i1.control.index], "foreign") l.set_parameters(bandwidth = 13107200) # 100 mbits tcdata = nemu.iproute.get_tc_data()[0] self.assertEquals(tcdata[i1.control.index], {"bandwidth": 13107000, "qdiscs": {"tbf": "1"}}) # Test tc replacements self._test_tbf() # none => tbf self._test_both() # tbf => both self._test_netem() # both => netem self._test_tbf() # netem => tbf self._test_netem() # tbf => netem self._test_none() # netem => none self._test_netem() # none => netem self._test_both() # netem => both self._test_tbf() # both => tbf self._test_none() # tbf => none self._test_both() # none => both self._test_none() # both => none def _test_none(self): (n1, n2, i1, i2, l) = self.stuff l.set_parameters() tcdata = nemu.iproute.get_tc_data()[0] self.assertEquals(tcdata[i1.control.index], {"qdiscs": {}}) self.assertEquals(tcdata[i2.control.index], {"qdiscs": {}}) def _test_tbf(self): (n1, n2, i1, i2, l) = self.stuff l.set_parameters(bandwidth = 13107200) # 100 mbits tcdata = nemu.iproute.get_tc_data()[0] self.assertEquals(tcdata[i1.control.index], # adjust for tc rounding {"bandwidth": 13107000, "qdiscs": {"tbf": "1"}}) self.assertEquals(tcdata[i2.control.index], {"bandwidth": 13107000, "qdiscs": {"tbf": "1"}}) def _test_netem(self): (n1, n2, i1, i2, l) = self.stuff l.set_parameters(delay = 0.001) # 1ms tcdata = nemu.iproute.get_tc_data()[0] self.assertEquals(tcdata[i1.control.index], {"delay": 0.001, "qdiscs": {"netem": "2"}}) self.assertEquals(tcdata[i2.control.index], {"delay": 0.001, "qdiscs": {"netem": "2"}}) def _test_both(self): (n1, n2, i1, i2, l) = self.stuff l.set_parameters(bandwidth = 13107200, delay = 0.001) # 100 mbits, 1ms tcdata = nemu.iproute.get_tc_data()[0] self.assertEquals(tcdata[i1.control.index], {"bandwidth": 13107000, "delay": 0.001, "qdiscs": {"tbf": "1", "netem": "2"}}) self.assertEquals(tcdata[i2.control.index], {"bandwidth": 13107000, "delay": 0.001, "qdiscs": {"tbf": "1", "netem": "2"}}) if __name__ == "__main__": unittest.main() nemu-0.3.1/test/test_util.py000066400000000000000000000055171300327754000160350ustar00rootroot00000000000000#!/usr/bin/env python # vim:ts=4:sw=4:et:ai:sts=4 import os, re, subprocess, sys import nemu.subprocess_ from nemu.environ import * def process_ipcmd(str): cur = None out = {} for line in str.split("\n"): if line == "": cur = None continue match = re.search(r'^(\d+): ([^@\s]+)(?:@\S+)?: <(\S+)> mtu (\d+) ' r'qdisc (\S+)', line) if match != None: cur = match.group(2) out[cur] = { 'idx': int(match.group(1)), 'flags': match.group(3).split(","), 'mtu': int(match.group(4)), 'qdisc': match.group(5), 'addr': [] } out[cur]['up'] = 'UP' in out[cur]['flags'] continue # Assume cur is defined assert cur != None match = re.search(r'^\s+link/\S*(?: ([0-9a-f:]+))?(?: |$)', line) if match != None: out[cur]['lladdr'] = match.group(1) continue match = re.search(r'^\s+inet ([0-9.]+)/(\d+)(?: brd ([0-9.]+))?', line) if match != None: out[cur]['addr'].append({ 'address': match.group(1), 'prefix_len': int(match.group(2)), 'broadcast': match.group(3), 'family': 'inet'}) continue match = re.search(r'^\s+inet6 ([0-9a-f:]+)/(\d+)(?: |$)', line) if match != None: out[cur]['addr'].append({ 'address': match.group(1), 'prefix_len': int(match.group(2)), 'family': 'inet6'}) continue match = re.search(r'^\s{4}', line) assert match != None return out def get_devs(): outdata = backticks([IP_PATH, "addr", "list"]) return process_ipcmd(outdata) def get_devs_netns(node): out = nemu.subprocess_.backticks_raise(node, [IP_PATH, "addr", "list"]) return process_ipcmd(out) def make_linux_ver(string): match = re.match(r'(\d+)\.(\d+)(?:\.(\d+))?(.*)', string) version, patchlevel, sublevel, extraversion = match.groups() if not sublevel: sublevel = 0 return (int(version) << 16) + (int(patchlevel) << 8) + int(sublevel) def get_linux_ver(): return make_linux_ver(os.uname()[2]) # Unittest from Python 2.6 doesn't have these decorators def _bannerwrap(f, text): name = f.__name__ def banner(*args, **kwargs): sys.stderr.write("*** WARNING: Skipping test %s: `%s'\n" % (name, text)) return None return banner def skip(text): return lambda f: _bannerwrap(f, text) def skipUnless(cond, text): return (lambda f: _bannerwrap(f, text)) if not cond else lambda f: f def skipIf(cond, text): return (lambda f: _bannerwrap(f, text)) if cond else lambda f: f