pyroute2-0.3.5/0000775000175000017500000000000012474420674013240 5ustar peetpeet00000000000000pyroute2-0.3.5/LICENSE0000664000175000017500000004325412432725447014255 0ustar peetpeet00000000000000 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. pyroute2-0.3.5/README.make.md0000664000175000017500000000251612432725455015436 0ustar peetpeet00000000000000Makefile documentation ====================== Makefile is used to automate Pyroute2 deployment and test processes. Mostly, it is but a collection of common commands. target: clean ------------- Clean up the repo directory from the built documentation, collected coverage data, compiled bytecode etc. target: docs ------------ Build documentation. Requires `Sphinx`. target: test ------------ Run tests against current code. Requires `flake8`, `nosetests`, `coverage`. Command line options: * python -- the Python to use * nosetests -- nosetests to use * wlevel -- the Python -W levels (see Makefile for description) * coverage -- whether to produce html coverage * pdb -- whether to run pdb on errors and failures Sample:: $ sudo make test python=python3 coverage=true wlevel=all Please notice, that by default tests run with wlevel=error, thus failing on *any* warning. target: dist ------------ Make Python distribution package. Command line options: * python -- the Python to use target: install --------------- Buidl and install the package into the system. Command line options: * python -- the Python to use * root -- root install directory * lib -- where to install lib files other targets ------------- Other targets are either utility targets to be used internally, or hooks for related projects. You can safely ignore them. pyroute2-0.3.5/README.packages.md0000664000175000017500000000053112433052117016257 0ustar peetpeet00000000000000Packaging infrastucture ======================= All the package-specific scripts, configs etc. should be placed under `packages` directory. It is better to use distutils tarball in packaging. packages/RedHat --------------- One common spec for all the RedHat flavours. To create RedHat package for the current running distro, run `make rpm`. pyroute2-0.3.5/README.md0000664000175000017500000000647412447250470014525 0ustar peetpeet00000000000000pyroute2 ======== Pyroute2 is a pure Python **netlink** and Linux **network configuration** library. It requires only Python stdlib, no 3rd party libraries. Later it can change, but the deps tree will remain as simple, as it is possible. The library provides several modules: 1. Netlink protocol implementations (RTNetlink, TaskStats, etc) 2. Simple netlink socket object, that can be used in poll/select 3. Network configuration module IPRoute provides API that in some way resembles ip/tc functionality 4. IPDB is an async transactional database of Linux network settings rtnetlink sample ---------------- More samples you can read in the project documentation. The lowest possible layer, simple socket interface. This socket supports normal socket API and can be used in poll/select:: from pyroute2 import IPRSocket # create the socket ip = IPRSocket() # bind ip.bind() # get and parse a broadcast message ip.get() # close ip.close() Low-level **IPRoute** utility -- Linux network configuration. **IPRoute** usually doesn't rely on external utilities, but in some cases, when the kernel doesn't provide the functionality via netlink (like on RHEL6.5), it transparently uses also brctl and sysfs to setup bridges and bonding interfaces:: from pyroute2 import IPRoute # get access to the netlink socket ip = IPRoute() # print interfaces print(ip.get_links()) # release Netlink socket ip.close() High-level transactional interface, **IPDB**, a network settings DB:: from pyroute2 import IPDB # local network settings ip = IPDB() # create bridge and add ports and addresses # transaction will be started with `with` statement # and will be committed at the end of the block try: with ip.create(kind='bridge', ifname='rhev') as i: i.add_port(ip.interfaces.em1) i.add_port(ip.interfaces.em2) i.add_ip('10.0.0.2/24') except Exception as e: print(e) finally: ip.release() The project contains several modules for different types of netlink messages, not only RTNL. network namespace samples ------------------------- Network namespace manipulation:: from pyroute2 import netns # create netns netns.create('test') # list print(netns.listnetns()) # remove netns netns.remove('test') Create **veth** interfaces pair and move to **netns**:: from pyroute2 import IPDB ip = IPDB() # create interface pair ip.create(ifname='v0p0', kind='veth', peer='v0p1').commit() # move peer to netns with ip.interfaces.v0p1 as veth: veth.net_ns_fd = 'test' # don't forget to release before exit ip.release() List interfaces in some **netns**:: from pyroute2 import NetNS from pprint import pprint ns = NetNS('test') pprint(ns.get_links()) ns.close() More details and samples see in the documentation. installation ------------ `make install` or `pip install pyroute2` requires -------- Python >= 2.6 The pyroute2 testing framework requires **flake8**, **coverage**, **nosetests**. links ----- * home: https://github.com/svinota/pyroute2 * bugs: https://github.com/svinota/pyroute2/issues * pypi: https://pypi.python.org/pypi/pyroute2 * docs: http://docs.pyroute2.org/ * list: https://groups.google.com/d/forum/pyroute2-dev pyroute2-0.3.5/PKG-INFO0000664000175000017500000001211412474420674014334 0ustar peetpeet00000000000000Metadata-Version: 1.1 Name: pyroute2 Version: 0.3.5 Summary: Python Netlink library Home-page: https://github.com/svinota/pyroute2 Author: Peter V. Saveliev Author-email: peter@svinota.eu License: GPLv2+ Description: pyroute2 ======== Pyroute2 is a pure Python **netlink** and Linux **network configuration** library. It requires only Python stdlib, no 3rd party libraries. Later it can change, but the deps tree will remain as simple, as it is possible. The library provides several modules: 1. Netlink protocol implementations (RTNetlink, TaskStats, etc) 2. Simple netlink socket object, that can be used in poll/select 3. Network configuration module IPRoute provides API that in some way resembles ip/tc functionality 4. IPDB is an async transactional database of Linux network settings rtnetlink sample ---------------- More samples you can read in the project documentation. The lowest possible layer, simple socket interface. This socket supports normal socket API and can be used in poll/select:: from pyroute2 import IPRSocket # create the socket ip = IPRSocket() # bind ip.bind() # get and parse a broadcast message ip.get() # close ip.close() Low-level **IPRoute** utility -- Linux network configuration. **IPRoute** usually doesn't rely on external utilities, but in some cases, when the kernel doesn't provide the functionality via netlink (like on RHEL6.5), it transparently uses also brctl and sysfs to setup bridges and bonding interfaces:: from pyroute2 import IPRoute # get access to the netlink socket ip = IPRoute() # print interfaces print(ip.get_links()) # release Netlink socket ip.close() High-level transactional interface, **IPDB**, a network settings DB:: from pyroute2 import IPDB # local network settings ip = IPDB() # create bridge and add ports and addresses # transaction will be started with `with` statement # and will be committed at the end of the block try: with ip.create(kind='bridge', ifname='rhev') as i: i.add_port(ip.interfaces.em1) i.add_port(ip.interfaces.em2) i.add_ip('10.0.0.2/24') except Exception as e: print(e) finally: ip.release() The project contains several modules for different types of netlink messages, not only RTNL. network namespace samples ------------------------- Network namespace manipulation:: from pyroute2 import netns # create netns netns.create('test') # list print(netns.listnetns()) # remove netns netns.remove('test') Create **veth** interfaces pair and move to **netns**:: from pyroute2 import IPDB ip = IPDB() # create interface pair ip.create(ifname='v0p0', kind='veth', peer='v0p1').commit() # move peer to netns with ip.interfaces.v0p1 as veth: veth.net_ns_fd = 'test' # don't forget to release before exit ip.release() List interfaces in some **netns**:: from pyroute2 import NetNS from pprint import pprint ns = NetNS('test') pprint(ns.get_links()) ns.close() More details and samples see in the documentation. installation ------------ `make install` or `pip install pyroute2` requires -------- Python >= 2.6 The pyroute2 testing framework requires **flake8**, **coverage**, **nosetests**. links ----- * home: https://github.com/svinota/pyroute2 * bugs: https://github.com/svinota/pyroute2/issues * pypi: https://pypi.python.org/pypi/pyroute2 * docs: http://docs.pyroute2.org/ * list: https://groups.google.com/d/forum/pyroute2-dev Platform: UNKNOWN Classifier: License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+) Classifier: Programming Language :: Python Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Operating System :: POSIX Classifier: Intended Audience :: Developers Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Development Status :: 4 - Beta pyroute2-0.3.5/CHANGELOG.md0000664000175000017500000001541112474420562015047 0ustar peetpeet00000000000000changelog ========= * 0.3.5 * netns: #90 -- netns setns support * generic: #99 -- support custom basic netlink socket classes * proxy-ng: #106 -- provide more diagnostics * nl80211: initial nl80211 support, iwutil module added * 0.3.4 * ipdb: #92 -- route metrics support * ipdb: #85 -- broadcast address specification * ipdb, rtnl: #84 -- veth support * ipdb, rtnl: tuntap support * netns: #84 -- network namespaces support, NetNS class * rtnl: proxy-ng API * pypi: #91 -- embed docs into the tarball * 0.3.3 * ipdb: restart on error * generic: handle non-existing family case * [fix]: #80 -- Python 2.6 unicode vs -O bug workaround * 0.3.2 * simple socket architecture * all the protocols now are based on NetlinkSocket, see examples * rpc: deprecated * iocore: deprecated * iproute: single-threaded socket object * ipdb: restart on errors * rtnl: updated ifinfmsg policies * 0.3.1 * module structure refactored * new protocol: ipq * new protocol: nfnetlink / nf-queue * new protocol: generic * threadless sockets for all the protocols * 0.2.16 * prepare the transition to 0.3.x * 0.2.15 * ipdb: fr #63 -- interface settings freeze * ipdb: fr #50, #51 -- bridge & bond options (initial version) * RHEL7 support * [fix]: #52 -- HTB: correct rtab compilation * [fix]: #53 -- RHEL6.5 bridge races * [fix]: #55 -- IPv6 on bridges * [fix]: #58 -- vlans as bridge ports * [fix]: #59 -- threads sync in iocore * 0.2.14 * [fix]: #44 -- incorrect netlink exceptions proxying * [fix]: #45 -- multiple issues with device targets * [fix]: #46 -- consistent exceptions * ipdb: LinkedSet cascade updates fixed * ipdb: allow to reuse existing interface in `create()` * 0.2.13 * [fix]: #43 -- pipe leak in the main I/O loop * tests: integrate examples, import into tests * iocore: use own TimeoutException instead of Queue.Empty * iproute: default routing table = 254 * iproute: flush_routes() routine * iproute: fwmark parameter for rule() routine * iproute: destination and mask for rules * docs: netlink development guide * 0.2.12 * [fix]: #33 -- release resources only for bound sockets * [fix]: #37 -- fix commit targets * rtnl: HFSC support * rtnl: priomap fixed * 0.2.11 * ipdb: watchdogs to sync on RTNL events * ipdb: fix commit errors * generic: NLA operations, complement and intersection * docs: more autodocs in the code * tests: -W error: more strict testing now * tests: cover examples by the integration testing cycle * with -W error many resource leaks were fixed * 0.2.10 * ipdb: command chaining * ipdb: fix for RHEL6.5 Python "optimizations" * rtnl: support TCA_U32_ACT * [fix]: #32 -- NLA comparison * 0.2.9 * ipdb: support bridges and bonding interfaces on RHEL * ipdb: "shadow" interfaces (still in alpha state) * ipdb: minor fixes on routing and compat issues * ipdb: as a separate package (sub-module) * docs: include ipdb autodocs * rpc: include in setup.py * 0.2.8 * netlink: allow multiple NetlinkSocket allocation from one process * netlink: fix defragmentation for netlink-over-tcp * iocore: support forked IOCore and IOBroker as a separate process * ipdb: generic callbacks support * ipdb: routing support * rtnl: #30 -- support IFLA_INFO_DATA for bond interfaces * 0.2.7 * ipdb: use separate namespaces for utility functions and other stuff * ipdb: generic callbacks (see also IPDB.wait_interface()) * iocore: initial multipath support * iocore: use of 16byte uuid4 for packet ids * 0.2.6 * rpc: initial version, REQ/REP, PUSH/PULL * iocore: shared IOLoop * iocore: AddrPool usage * iproute: policing in FW filter * python3 compatibility issues fixed * 0.2.4 * python3 compatibility issues fixed, tests passed * 0.2.3 * [fix]: #28 -- bundle issue * 0.2.2 * iocore: new component * iocore: separate IOCore and IOBroker * iocore: change from peer-to-peer to flat addresses * iocore: REP/REQ, PUSH/PULL * iocore: support for UDP PUSH/PULL * iocore: AddrPool component for addresses and nonces * generic: allow multiple re-encoding * 0.1.12 * ipdb: transaction commit callbacks * iproute: delete root qdisc (@chantra) * iproute: netem qdisc management (@chantra) * 0.1.11 * netlink: get qdiscs for particular interface * netlink: IPRSocket threadless objects * rtnl: u32 policy setup * iproute: filter actions, such as `ok`, `drop` and so on * iproute: changed syntax of commands, `action` → `command` * tests: htb, tbf tests added * 0.1.10 * [fix]: #8 -- default route fix, routes filtering * [fix]: #9 -- add/delete route routine improved * [fix]: #10 -- shutdown sequence fixed * [fix]: #11 -- close IPC pipes on release() * [fix]: #12 -- stop service threads on release() * netlink: debug mode added to be used with GUI * ipdb: interface removal * ipdb: fail on transaction sync timeout * tests: R/O mode added, use `export PYROUTE2_TESTS_RO=True` * 0.1.9 * tests: all races fixed * ipdb: half-sync commit(): wait for IPs and ports lists update * netlink: use pipes for in-process communication * Python 2.6 compatibility issue: remove copy.deepcopy() usage * QPython 2.7 for Android: works * 0.1.8 * complete refactoring of class names * Python 2.6 compatibility issues * tests: code coverage, multiple code fixes * plugins: ptrace message source * packaging: RH package * 0.1.7 * ipdb: interface creation: dummy, bond, bridge, vlan * ipdb: if\_slaves interface obsoleted * ipdb: 'direct' mode * iproute: code refactored * examples: create() examples committed * 0.1.6 * netlink: tc ingress, sfq, tbf, htb, u32 partial support * ipdb: completely re-implemented transactional model (see docs) * generic: internal fields declaration API changed for nlmsg * tests: first unit tests committed * 0.1.5 * netlink: dedicated io buffering thread * netlink: messages reassembling * netlink: multi-uplink remote * netlink: masquerade remote requests * ipdb: represent interfaces hierarchy * iproute: decode VLAN info * 0.1.4 * netlink: remote netlink access * netlink: SSL/TLS server/client auth support * netlink: tcp and unix transports * docs: started sphinx docs * 0.1.3 * ipdb: context manager interface * ipdb: [fix] correctly handle ip addr changes in transaction * ipdb: [fix] make up()/down() methods transactional [#1] * iproute: mirror packets to 0 queue * iproute: [fix] handle primary ip address removal response * 0.1.2 * initial ipdb version * iproute fixes * 0.1.1 * initial release, iproute module pyroute2-0.3.5/setup.py0000664000175000017500000000247212474420673014756 0ustar peetpeet00000000000000#!/usr/bin/env python from distutils.core import setup readme = open("README.md", "r") setup(name='pyroute2', version='0.3.5', description='Python Netlink library', author='Peter V. Saveliev', author_email='peter@svinota.eu', url='https://github.com/svinota/pyroute2', license='GPLv2+', packages=['pyroute2', 'pyroute2.ipdb', 'pyroute2.netlink', 'pyroute2.netlink.generic', 'pyroute2.netlink.ipq', 'pyroute2.netlink.nfnetlink', 'pyroute2.netlink.rtnl', 'pyroute2.netlink.taskstats', 'pyroute2.netlink.nl80211'], classifiers=['License :: OSI Approved :: GNU General Public ' + 'License v2 or later (GPLv2+)', 'Programming Language :: Python', 'Topic :: Software Development :: Libraries :: ' + 'Python Modules', 'Operating System :: POSIX', 'Intended Audience :: Developers', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Development Status :: 4 - Beta'], long_description=readme.read()) pyroute2-0.3.5/pyroute2/0000775000175000017500000000000012474420674015031 5ustar peetpeet00000000000000pyroute2-0.3.5/pyroute2/ipdb/0000775000175000017500000000000012474420674015747 5ustar peetpeet00000000000000pyroute2-0.3.5/pyroute2/ipdb/route.py0000664000175000017500000001421312453321755017455 0ustar peetpeet00000000000000import threading from socket import AF_UNSPEC from pyroute2.netlink.rtnl.rtmsg import rtmsg from pyroute2.netlink.rtnl.req import IPRouteRequest from pyroute2.ipdb.transactional import Transactional class Metrics(Transactional): def __init__(self, *argv, **kwarg): Transactional.__init__(self, *argv, **kwarg) self._fields = [rtmsg.metrics.nla2name(i[0]) for i in rtmsg.metrics.nla_map] class Route(Transactional): def __init__(self, ipdb, mode=None, parent=None, uid=None): Transactional.__init__(self, ipdb, mode, parent, uid) self._exists = False self._load_event = threading.Event() self._fields = [rtmsg.nla2name(i[0]) for i in rtmsg.nla_map] self._fields.append('flags') self._fields.append('src_len') self._fields.append('dst_len') self._fields.append('table') self._fields.append('removal') self.cleanup = ('attrs', 'header', 'event') with self._direct_state: self['metrics'] = Metrics(parent=self) def load_netlink(self, msg): with self._direct_state: self._exists = True self.update(msg) # merge key for (name, value) in msg['attrs']: norm = rtmsg.nla2name(name) # normalize RTAX if norm == 'metrics': ret = self.get(norm, Metrics(parent=self)) with ret._direct_state: for (rtax, rtax_value) in value['attrs']: rtax_norm = rtmsg.metrics.nla2name(rtax) ret[rtax_norm] = rtax_value self[norm] = ret else: self[norm] = value if msg.get_attr('RTA_DST', None) is not None: dst = '%s/%s' % (msg.get_attr('RTA_DST'), msg['dst_len']) else: dst = 'default' self['dst'] = dst # finally, cleanup all not needed for item in self.cleanup: if item in self: del self[item] self.sync() def sync(self): self._load_event.set() def reload(self): # do NOT call get_routes() here, it can cause race condition self._load_event.wait() return self def commit(self, tid=None, transaction=None, rollback=False): self._load_event.clear() error = None if tid: transaction = self._transactions[tid] else: transaction = transaction or self.last() # create a new route if not self._exists: try: self.nl.route('add', **IPRouteRequest(self)) except Exception: self.nl = None self.ipdb.routes.remove(self) raise # work on existing route snapshot = self.pick() try: # route set request = IPRouteRequest(transaction - snapshot) if any([request[x] not in (None, {'attrs': []}) for x in request]): self.nl.route('set', **IPRouteRequest(transaction)) if transaction.get('removal'): self.nl.route('delete', **IPRouteRequest(snapshot)) except Exception as e: if not rollback: ret = self.commit(transaction=snapshot, rollback=True) if isinstance(ret, Exception): error = ret else: error = e else: self.drop() x = RuntimeError() x.cause = e raise x if not rollback: self.drop() self.reload() if error is not None: error.transaction = transaction raise error return self def remove(self): self['removal'] = True return self class RoutingTables(dict): def __init__(self, ipdb): dict.__init__(self) self.ipdb = ipdb self.tables = {254: {}} def add(self, spec=None, **kwarg): ''' Create a route from a dictionary ''' spec = spec or kwarg table = spec.get('table', 254) assert 'dst' in spec route = Route(self.ipdb) metrics = spec.pop('metrics', {}) route.update(spec) route.metrics.update(metrics) if table not in self.tables: self.tables[table] = dict() self.tables[table][route['dst']] = route route.begin() return route def load_netlink(self, msg): ''' Loads an existing route from a rtmsg ''' table = msg.get('table', 254) if table not in self.tables: self.tables[table] = dict() dst = msg.get_attr('RTA_DST', None) if dst is None: key = 'default' else: key = '%s/%s' % (dst, msg.get('dst_len', 0)) if key in self.tables[table]: ret = self.tables[table][key] ret.load_netlink(msg) else: ret = Route(ipdb=self.ipdb) ret.load_netlink(msg) self.tables[table][key] = ret return ret def remove(self, route, table=None): if isinstance(route, Route): table = route.get('table', 254) route = route.get('dst', 'default') else: table = table or 254 del self.tables[table][route] def get(self, dst, table=None): table = table or 254 return self.tables[table][dst] def keys(self, table=254, family=AF_UNSPEC): return [x['dst'] for x in self.tables[table].values() if (x['family'] == family) or (family == AF_UNSPEC)] def has_key(self, key, table=254): return key in self.tables[table] def __contains__(self, key): return key in self.tables[254] def __getitem__(self, key): return self.get(key) def __setitem__(self, key, value): assert key == value['dst'] return self.add(value) def __delitem__(self, key): return self.remove(key) pyroute2-0.3.5/pyroute2/ipdb/__init__.py0000664000175000017500000007171312471077567020077 0ustar peetpeet00000000000000# -*- coding: utf-8 -*- ''' IPDB module =========== Basically, IPDB is a transactional database, containing records, representing network stack objects. Any change in the database is not reflected immediately in OS (unless you ask for that explicitly), but waits until `commit()` is called. One failed operation during `commit()` rolls back all the changes, has been made so far. Moreover, IPDB has commit hooks API, that allows you to roll back changes depending on your own function calls, e.g. when a host or a network becomes unreachable. IPDB vs. IPRoute ---------------- These two modules, IPRoute and IPDB, use completely different approaches. The first one, IPRoute, is synchronous by default, and can be used in the same way, as usual Linux utilities. It doesn't spawn any additional threads or processes, until you explicitly ask for that. The latter, IPDB, is an asynchronously updated database, that starts several additional threads by default. If your project's policy doesn't allow implicit threads, keep it in mind. The choice depends on your project's workflow. If you plan to retrieve the system info not too often (or even once), or you are sure there will be not too many network object, it is better to use IPRoute. If you plan to lookup the network info on a regular basis and there can be loads of network objects, it is better to use IPDB. Why? IPRoute just loads what you ask -- and loads all the information you ask to. While IPDB loads all the info upon startup, and later is just updated by asynchronous broadcast netlink messages. Assume you want to lookup ARP cache that contains hundreds or even thousands of objects. Using IPRoute, you have to load all the ARP cache every time you want to make a lookup. While IPDB will load all the cache once, and then maintain it up-to-date just inserting new records or removing them by one. So, IPRoute is much simpler when you need to make a call and then exit. While IPDB is cheaper in terms of CPU performance if you implement a long-running program like a daemon. Later it can change, if there will be (an optional) cache for IPRoute too. quickstart ---------- Simple tutorial:: from pyroute2 import IPDB # several IPDB instances are supported within on process ip = IPDB() # commit is called automatically upon the exit from `with` # statement with ip.interfaces.eth0 as i: i.address = '00:11:22:33:44:55' i.ifname = 'bala' i.txqlen = 2000 # basic routing support ip.routes.add({'dst': 'default', 'gateway': '10.0.0.1'}).commit() # do not forget to shutdown IPDB ip.release() Please, notice `ip.release()` call in the end. Though it is not forced in an interactive python session for the better user experience, it is required in the scripts to sync the IPDB state before exit. IPDB uses IPRoute as a transport, and monitors all broadcast netlink messages from the kernel, thus keeping the database up-to-date in an asynchronous manner. IPDB inherits `dict` class, and has two keys:: >>> from pyroute2 import IPDB >>> ip = IPDB() >>> ip.by_name.keys() ['bond0', 'lo', 'em1', 'wlan0', 'dummy0', 'virbr0-nic', 'virbr0'] >>> ip.by_index.keys() [32, 1, 2, 3, 4, 5, 8] >>> ip.interfaces.keys() [32, 1, 2, 3, 4, 5, 8, 'lo', 'em1', 'wlan0', 'bond0', 'dummy0', 'virbr0-nic', 'virbr0'] >>> ip.interfaces['em1']['address'] 'f0:de:f1:93:94:0d' >>> ip.interfaces['em1']['ipaddr'] [('10.34.131.210', 23), ('2620:52:0:2282:f2de:f1ff:fe93:940d', 64), ('fe80::f2de:f1ff:fe93:940d', 64)] >>> One can address objects in IPDB not only with dict notation, but with dot notation also:: >>> ip.interfaces.em1.address 'f0:de:f1:93:94:0d' >>> ip.interfaces.em1.ipaddr [('10.34.131.210', 23), ('2620:52:0:2282:f2de:f1ff:fe93:940d', 64), ('fe80::f2de:f1ff:fe93:940d', 64)] ``` It is up to you, which way to choose. The former, being more flexible, is better for developers, the latter, the shorter form -- for system administrators. The library has also IPDB module. It is a database synchronized with the kernel, containing some of the information. It can be used also to set up IP settings in a transactional manner: >>> from pyroute2 import IPDB >>> from pprint import pprint >>> ip = IPDB() >>> pprint(ip.by_name.keys()) ['bond0', 'lo', 'vnet0', 'em1', 'wlan0', 'macvtap0', 'dummy0', 'virbr0-nic', 'virbr0'] >>> ip.interfaces.lo {'promiscuity': 0, 'operstate': 'UNKNOWN', 'qdisc': 'noqueue', 'group': 0, 'family': 0, 'index': 1, 'linkmode': 0, 'ipaddr': [('127.0.0.1', 8), ('::1', 128)], 'mtu': 65536, 'broadcast': '00:00:00:00:00:00', 'num_rx_queues': 1, 'txqlen': 0, 'ifi_type': 772, 'address': '00:00:00:00:00:00', 'flags': 65609, 'ifname': 'lo', 'num_tx_queues': 1, 'ports': [], 'change': 0} >>> transaction modes ----------------- IPDB has several operating modes: * 'direct' -- any change goes immediately to the OS level * 'implicit' (default) -- the first change starts an implicit transaction, that have to be committed * 'explicit' -- you have to begin() a transaction prior to make any change * 'snapshot' -- no changes will go to the OS in any case The default is to use implicit transaction. This behaviour can be changed in the future, so use 'mode' argument when creating IPDB instances. The sample session with explicit transactions:: In [1]: from pyroute2 import IPDB In [2]: ip = IPDB(mode='explicit') In [3]: ifdb = ip.interfaces In [4]: ifdb.tap0.begin() Out[3]: UUID('7a637a44-8935-4395-b5e7-0ce40d31d937') In [5]: ifdb.tap0.up() In [6]: ifdb.tap0.address = '00:11:22:33:44:55' In [7]: ifdb.tap0.add_ip('10.0.0.1', 24) In [8]: ifdb.tap0.add_ip('10.0.0.2', 24) In [9]: ifdb.tap0.review() Out[8]: {'+ipaddr': set([('10.0.0.2', 24), ('10.0.0.1', 24)]), '-ipaddr': set([]), 'address': '00:11:22:33:44:55', 'flags': 4099} In [10]: ifdb.tap0.commit() Note, that you can `review()` the `last()` transaction, and `commit()` or `drop()` it. Also, multiple `self._transactions` are supported, use uuid returned by `begin()` to identify them. Actually, the form like 'ip.tap0.address' is an eye-candy. The IPDB objects are dictionaries, so you can write the code above as that:: ip.interfaces['tap0'].down() ip.interfaces['tap0']['address'] = '00:11:22:33:44:55' ... context managers ---------------- Also, interface objects in transactional mode can operate as context managers:: with ip.interfaces.tap0 as i: i.address = '00:11:22:33:44:55' i.ifname = 'vpn' i.add_ip('10.0.0.1', 24) i.add_ip('10.0.0.1', 24) On exit, the context manager will authomatically `commit()` the transaction. interface creation ------------------ IPDB can also create interfaces:: with ip.create(kind='bridge', ifname='control') as i: i.add_port(ip.interfaces.eth1) i.add_port(ip.interfaces.eth2) i.add_ip('10.0.0.1/24') # the same as i.add_ip('10.0.0.1', 24) Right now IPDB supports creation of `dummy`, `bond`, `bridge` and `vlan` interfaces. VLAN creation requires also `link` and `vlan_id` parameters, see example scripts. routing management ------------------ IPDB has a simple yet useful routing management interface. To add a route, one can use almost any syntax:: # spec as a dictionary spec = {'dst': '172.16.1.0/24', 'oif': 4, 'gateway': '192.168.122.60', 'metrics': {'mtu': 1400, 'advmss': 500}} # pass spec as is ip.routes.add(spec).commit() # pass spec as kwargs ip.routes.add(**spec).commit() # use keyword arguments explicitly ip.routes.add(dst='172.16.1.0/24', oif=4, ...).commit() To access and change the routes, one can use notations as follows:: # default table (254) # # change the route gateway and mtu # with ip.routes['172.16.1.0/24'] as route: route.gateway = '192.168.122.60' route.metrics.mtu = 1500 # access the default route print(ip.routes['default]) # change the default gateway with ip.routes['default'] as route: route.gateway = '10.0.0.1' # list automatic routes keys print(ip.routes.tables[255].keys()) performance issues ------------------ In the case of bursts of Netlink broadcast messages, all the activity of the pyroute2-based code in the async mode becomes suppressed to leave more CPU resources to the packet reader thread. So please be ready to cope with delays in the case of Netlink broadcast storms. It means also, that IPDB state will be synchronized with OS also after some delay. classes ------- ''' import sys import atexit import logging import traceback import threading from socket import AF_INET from socket import AF_INET6 from pyroute2.common import Dotkeys from pyroute2.iproute import IPRoute from pyroute2.netlink.rtnl import RTM_GETLINK from pyroute2.ipdb.common import CreateException from pyroute2.ipdb.interface import Interface from pyroute2.ipdb.linkedset import LinkedSet from pyroute2.ipdb.linkedset import IPaddrSet from pyroute2.ipdb.common import compat from pyroute2.ipdb.common import SYNC_TIMEOUT from pyroute2.ipdb.route import RoutingTables def get_addr_nla(msg): ''' Utility function to get NLA, containing the interface address. Incosistency in Linux IP addressing scheme is that IPv4 uses IFA_LOCAL to store interface's ip address, and IPv6 uses for the same IFA_ADDRESS. IPv4 sets IFA_ADDRESS to == IFA_LOCAL or to a tunneling endpoint. Args: * msg (nlmsg): RTM\_.*ADDR message Returns: * nla (nla): IFA_LOCAL for IPv4 and IFA_ADDRESS for IPv6 ''' nla = None if msg['family'] == AF_INET: nla = msg.get_attr('IFA_LOCAL') elif msg['family'] == AF_INET6: nla = msg.get_attr('IFA_ADDRESS') return nla class Watchdog(object): def __init__(self, ipdb, action, kwarg): self.event = threading.Event() self.ipdb = ipdb def cb(ipdb, msg, _action): if _action != action: return for key in kwarg: if (msg.get(key, None) != kwarg[key]) and \ (msg.get_attr(msg.name2nla(key)) != kwarg[key]): return self.event.set() self.cb = cb # register callback prior to other things self.ipdb.register_callback(self.cb) def wait(self, timeout=SYNC_TIMEOUT): self.event.wait(timeout=timeout) self.cancel() def cancel(self): self.ipdb.unregister_callback(self.cb) class IPDB(object): ''' The class that maintains information about network setup of the host. Monitoring netlink events allows it to react immediately. It uses no polling. ''' def __init__(self, nl=None, mode='implicit', restart_on_error=None): ''' Parameters: * nl -- IPRoute() reference * mode -- (implicit, explicit, direct) * iclass -- the interface class type If you do not provide iproute instance, ipdb will start it automatically. ''' self.mode = mode self.iclass = Interface self._stop = False # see also 'register_callback' self._post_callbacks = [] self._pre_callbacks = [] self._cb_threads = set() # locks and events self._links_event = threading.Event() self.exclusive = threading.RLock() self._shutdown_lock = threading.Lock() # load information self.restart_on_error = restart_on_error if \ restart_on_error is not None else nl is None self.initdb(nl) # start monitoring thread self._mthread = threading.Thread(target=self.serve_forever) if hasattr(sys, 'ps1'): self._mthread.setDaemon(True) self._mthread.start() # atexit.register(self.release) def __enter__(self): return self def __exit__(self, exc_type, exc_value, traceback): self.release() def initdb(self, nl=None): ''' Restart IPRoute channel, and create all the DB from scratch. Can be used when sync is lost. ''' self.nl = nl or IPRoute() self.nl.monitor = True self.nl.bind(async=True) # resolvers self.interfaces = Dotkeys() self.routes = RoutingTables(ipdb=self) self.by_name = Dotkeys() self.by_index = Dotkeys() # caches self.ipaddr = {} self.neighbors = {} # load information links = self.nl.get_links() for link in links: self.device_put(link, skip_slaves=True) for link in links: self.update_slaves(link) self.update_addr(self.nl.get_addr()) self.update_neighbors(self.nl.get_neighbors()) routes = self.nl.get_routes() self.update_routes(routes) def register_callback(self, callback, mode='post'): ''' IPDB callbacks are routines executed on a RT netlink message arrival. There are two types of callbacks: "post" and "pre" callbacks. ... "Post" callbacks are executed after the message is processed by IPDB and all corresponding objects are created or deleted. Using ipdb reference in "post" callbacks you will access the most up-to-date state of the IP database. "Post" callbacks are executed asynchronously in separate threads. These threads can work as long as you want them to. Callback threads are joined occasionally, so for a short time there can exist stopped threads. ... "Pre" callbacks are synchronous routines, executed before the message gets processed by IPDB. It gives you the way to patch arriving messages, but also places a restriction: until the callback exits, the main event IPDB loop is blocked. Normally, only "post" callbacks are required. But in some specific cases "pre" also can be useful. ... The routine, `register_callback()`, takes two arguments: 1. callback function 2. mode (optional, default="post") The callback should be a routine, that accepts three arguments:: cb(ipdb, msg, action) 1. ipdb is a reference to IPDB instance, that invokes the callback. 2. msg is a message arrived 3. action is just a msg['event'] field E.g., to work on a new interface, you should catch action == 'RTM_NEWLINK' and with the interface index (arrived in msg['index']) get it from IPDB:: index = msg['index'] interface = ipdb.interfaces[index] ''' lock = threading.Lock() def safe(*argv, **kwarg): with lock: callback(*argv, **kwarg) safe.hook = callback if mode == 'post': self._post_callbacks.append(safe) elif mode == 'pre': self._pre_callbacks.append(safe) def unregister_callback(self, callback, mode='post'): if mode == 'post': cbchain = self._post_callbacks elif mode == 'pre': cbchain = self._pre_callbacks else: raise KeyError('Unknown callback mode') for cb in tuple(cbchain): if callback == cb.hook: for t in tuple(self._cb_threads): t.join(3) return cbchain.pop(cbchain.index(cb)) def release(self): ''' Shutdown IPDB instance and sync the state. Since IPDB is asyncronous, some operations continue in the background, e.g. callbacks. So, prior to exit the script, it is required to properly shutdown IPDB. The shutdown sequence is not forced in an interactive python session, since it is easier for users and there is enough time to sync the state. But for the scripts the `release()` call is required. ''' with self._shutdown_lock: if self._stop: return self._stop = True try: self.nl.put({'index': 1}, RTM_GETLINK) self._mthread.join() except Exception: # Just give up. # We can not handle this case pass self.nl.close() def create(self, kind, ifname, reuse=False, **kwarg): ''' Create an interface. Arguments 'kind' and 'ifname' are required. * kind -- interface type, can be of: * bridge * bond * vlan * tun * dummy * veth * ifname -- interface name * reuse -- if such interface exists, return it anyway Different interface kinds can require different arguments for creation. ► **veth** To properly create `veth` interface, one should specify `peer` also, since `veth` interfaces are created in pairs:: with ip.create(ifname='v1p0', kind='veth', peer='v1p1') as i: i.add_ip('10.0.0.1/24') i.add_ip('10.0.0.2/24') The code above creates two interfaces, `v1p0` and `v1p1`, and adds two addresses to `v1p0`. ► **vlan** VLAN interfaces require additional parameters, `vlan_id` and `link`, where `link` is a master interface to create VLAN on:: ip.create(ifname='v100', kind='vlan', link=ip.interfaces.eth0, vlan_id=100) ip.create(ifname='v100', kind='vlan', link=1, vlan_id=100) The `link` parameter should be either integer, interface id, or an interface object. VLAN id must be integer. ► **tuntap** Possible `tuntap` keywords: * `mode` — "tun" or "tap" * `uid` — integer * `gid` — integer * `ifr` — dict of tuntap flags (see tuntapmsg.py) ''' with self.exclusive: # check for existing interface if ifname in self.interfaces: if self.interfaces[ifname]._flicker or reuse: device = self.interfaces[ifname] device._flicker = False else: raise CreateException("interface %s exists" % ifname) else: device = \ self.by_name[ifname] = \ self.interfaces[ifname] = \ self.iclass(ipdb=self, mode='snapshot') device.update(kwarg) if isinstance(kwarg.get('link', None), Interface): device['link'] = kwarg['link']['index'] device['kind'] = kind device['index'] = kwarg.get('index', 0) device['ifname'] = ifname device._mode = self.mode device.begin() return device def device_del(self, msg): # check for flicker devices if (msg.get('index', None) in self.interfaces) and \ self.interfaces[msg['index']]._flicker: self.interfaces[msg['index']].sync() return try: self.update_slaves(msg) if msg['change'] == 0xffffffff: # FIXME catch exception ifname = self.interfaces[msg['index']]['ifname'] self.interfaces[msg['index']].sync() del self.by_name[ifname] del self.by_index[msg['index']] del self.interfaces[ifname] del self.interfaces[msg['index']] del self.ipaddr[msg['index']] del self.neighbors[msg['index']] except KeyError: pass def device_put(self, msg, skip_slaves=False): # check, if a record exists index = msg.get('index', None) ifname = msg.get_attr('IFLA_IFNAME', None) # scenario #1: no matches for both: new interface # scenario #2: ifname exists, index doesn't: index changed # scenario #3: index exists, ifname doesn't: name changed # scenario #4: both exist: assume simple update and # an optional name change if ((index not in self.interfaces) and (ifname not in self.interfaces)): # scenario #1, new interface if compat.fix_check_link(self.nl, index): return device = \ self.by_index[index] = \ self.interfaces[index] = \ self.interfaces[ifname] = \ self.by_name[ifname] = self.iclass(ipdb=self) elif ((index not in self.interfaces) and (ifname in self.interfaces)): # scenario #2, index change old_index = self.interfaces[ifname]['index'] device = \ self.interfaces[index] = \ self.by_index[index] = self.interfaces[ifname] if old_index in self.interfaces: del self.interfaces[old_index] del self.by_index[old_index] if old_index in self.ipaddr: self.ipaddr[index] = self.ipaddr[old_index] del self.ipaddr[old_index] if old_index in self.neighbors: self.neighbors[index] = self.neighbors[old_index] del self.neighbors[old_index] else: # scenario #3, interface rename # scenario #4, assume rename old_name = self.interfaces[index]['ifname'] if old_name != ifname: # unlink old name del self.interfaces[old_name] del self.by_name[old_name] device = \ self.interfaces[ifname] = \ self.by_name[ifname] = self.interfaces[index] if index not in self.ipaddr: # for interfaces, created by IPDB self.ipaddr[index] = IPaddrSet() if index not in self.neighbors: self.neighbors[index] = LinkedSet() device.load_netlink(msg) if not skip_slaves: self.update_slaves(msg) def detach(self, item): with self.exclusive: if item in self.interfaces: del self.interfaces[item] if item in self.by_name: del self.by_name[item] if item in self.by_index: del self.by_index[item] def watchdog(self, action='RTM_NEWLINK', **kwarg): return Watchdog(self, action, kwarg) def update_routes(self, routes): for msg in routes: self.routes.load_netlink(msg) def _lookup_master(self, msg): master = msg.get_attr('IFLA_MASTER') return self.interfaces.get(master, None) def update_slaves(self, msg): # Update slaves list -- only after update IPDB! master = self._lookup_master(msg) index = msg['index'] # there IS a master for the interface if master is not None: if msg['event'] == 'RTM_NEWLINK': # TODO tags: ipdb # The code serves one particular case, when # an enslaved interface is set to belong to # another master. In this case there will be # no 'RTM_DELLINK', only 'RTM_NEWLINK', and # we can end up in a broken state, when two # masters refers to the same slave for device in self.by_index: if index in self.interfaces[device]['ports']: self.interfaces[device].del_port(index, direct=True) master.add_port(index, direct=True) elif msg['event'] == 'RTM_DELLINK': if index in master['ports']: master.del_port(index, direct=True) # there is NO masters for the interface, clean them if any else: device = self.interfaces[msg['index']] # clean device from ports for master in self.by_index: if index in self.interfaces[master]['ports']: self.interfaces[master].del_port(index, direct=True) master = device.if_master if master is not None: if 'master' in device: device.del_item('master') if (master in self.interfaces) and \ (msg['index'] in self.interfaces[master].ports): self.interfaces[master].del_port(msg['index'], direct=True) def update_addr(self, addrs, action='add'): # Update address list of an interface. for addr in addrs: nla = get_addr_nla(addr) if nla is not None: try: method = getattr(self.ipaddr[addr['index']], action) method(key=(nla, addr['prefixlen']), raw=addr) except: pass def update_neighbors(self, neighs, action='add'): for neigh in neighs: nla = neigh.get_attr('NDA_DST') if nla is not None: try: method = getattr(self.neighbors[neigh['ifindex']], action) method(key=nla, raw=neigh) except: pass def serve_forever(self): ''' Main monitoring cycle. It gets messages from the default iproute queue and updates objects in the database. .. note:: Should not be called manually. ''' while not self._stop: try: messages = self.nl.get() ## # Check it again # # NOTE: one should not run callbacks or # anything like that after setting the # _stop flag, since IPDB is not valid # anymore if self._stop: break except: logging.error('Restarting IPDB instance after ' 'error:\n%s', traceback.format_exc()) if self.restart_on_error: self.initdb() continue else: raise RuntimeError('Emergency shutdown') for msg in messages: # Run pre-callbacks # NOTE: pre-callbacks are synchronous for cb in self._pre_callbacks: try: cb(self, msg, msg['event']) except: pass with self.exclusive: if msg.get('event', None) == 'RTM_NEWLINK': self.device_put(msg) self._links_event.set() elif msg.get('event', None) == 'RTM_DELLINK': self.device_del(msg) elif msg.get('event', None) == 'RTM_NEWADDR': self.update_addr([msg], 'add') elif msg.get('event', None) == 'RTM_DELADDR': self.update_addr([msg], 'remove') elif msg.get('event', None) == 'RTM_NEWNEIGH': self.update_neighbors([msg], 'add') elif msg.get('event', None) == 'RTM_DELNEIGH': self.update_neighbors([msg], 'remove') elif msg.get('event', None) == 'RTM_NEWROUTE': self.update_routes([msg]) elif msg.get('event', None) == 'RTM_DELROUTE': table = msg.get('table', 254) dst = msg.get_attr('RTA_DST', False) if not dst: key = 'default' else: key = '%s/%s' % (dst, msg.get('dst_len', 0)) try: route = self.routes.tables[table][key] del self.routes.tables[table][key] route.sync() except KeyError: pass # run post-callbacks # NOTE: post-callbacks are asynchronous for cb in self._post_callbacks: t = threading.Thread(name="callback %s" % (id(cb)), target=cb, args=(self, msg, msg['event'])) t.start() self._cb_threads.add(t) # occasionally join cb threads for t in tuple(self._cb_threads): t.join(0) if not t.is_alive(): self._cb_threads.remove(t) pyroute2-0.3.5/pyroute2/ipdb/common.py0000664000175000017500000000206712432725455017615 0ustar peetpeet00000000000000import time from pyroute2.common import ANCIENT from pyroute2.netlink import NetlinkError # How long should we wait on EACH commit() checkpoint: for ipaddr, # ports etc. That's not total commit() timeout. SYNC_TIMEOUT = 5 class DeprecationException(Exception): pass class CommitException(Exception): pass class CreateException(Exception): pass def bypass(f): if ANCIENT: return f else: return staticmethod(lambda *x, **y: None) class compat(object): ''' A namespace to keep all compat-related methods. ''' @bypass @staticmethod def fix_timeout(timeout): time.sleep(timeout) @bypass @staticmethod def fix_check_link(nl, index): # check, if the link really exists -- # on some old kernels you can receive # broadcast RTM_NEWLINK after the link # was deleted try: nl.get_links(index) except NetlinkError as e: if e.code == 19: # No such device # just drop this message then return True pyroute2-0.3.5/pyroute2/ipdb/linkedset.py0000664000175000017500000000765612467374327020326 0ustar peetpeet00000000000000''' ''' import threading class LinkedSet(set): ''' Utility class, used by `Interface` to track ip addresses and ports. Called "linked" as it automatically updates all instances, linked with it. Target filter is a function, that returns `True` if a set member should be counted in target checks (target methods see below), or `False` if it should be ignored. ''' def target_filter(self, x): return True def __init__(self, *argv, **kwarg): set.__init__(self, *argv, **kwarg) self.lock = threading.RLock() self.target = threading.Event() self._ct = None self.raw = {} self.links = [] self.exclusive = set() def __getitem__(self, key): return self.raw[key] def set_target(self, value): ''' Set target state for the object and clear the target event. Once the target is reached, the event will be set, see also: `check_target()` Args: * value (set): the target state to compare with ''' with self.lock: if value is None: self._ct = None self.target.clear() else: self._ct = set(value) self.target.clear() # immediately check, if the target already # reached -- otherwise you will miss the # target forever self.check_target() def check_target(self): ''' Check the target state and set the target event in the case the state is reached. Called from mutators, `add()` and `remove()` ''' with self.lock: if self._ct is not None: if set(filter(self.target_filter, self)) == \ set(filter(self.target_filter, self._ct)): self._ct = None self.target.set() def add(self, key, raw=None, cascade=False): ''' Add an item to the set and all connected instances, check the target state. Args: * key: any hashable object * raw (optional): raw representation of the object Raw representation is not required. It can be used, e.g., to store RTM_NEWADDR RTNL messages along with human-readable ip addr representation. ''' with self.lock: if cascade and (key in self.exclusive): return if key not in self: self.raw[key] = raw set.add(self, key) for link in self.links: link.add(key, raw, cascade=True) self.check_target() def remove(self, key, raw=None, cascade=False): ''' Remove an item from the set and all connected instances, check the target state. ''' with self.lock: if cascade and (key in self.exclusive): return set.remove(self, key) for link in self.links: if key in link: link.remove(key, cascade=True) self.check_target() def unlink(self, key): ''' Exclude key from cascade updates. ''' self.exclusive.add(key) def relink(self, key): ''' Do not ignore key on cascade updates. ''' self.exclusive.remove(key) def connect(self, link): ''' Connect a LinkedSet instance to this one. Connected sets will be updated together with this instance. ''' assert isinstance(link, LinkedSet) self.links.append(link) def __repr__(self): return repr(list(self)) class IPaddrSet(LinkedSet): ''' LinkedSet child class with different target filter. The filter ignores link local IPv6 addresses when sets and checks the target. ''' def target_filter(self, x): return not ((x[0][:4] == 'fe80') and (x[1] == 64)) pyroute2-0.3.5/pyroute2/ipdb/interface.py0000664000175000017500000006023512472732376020272 0ustar peetpeet00000000000000import time import socket import threading import traceback from pyroute2.common import basestring from pyroute2.common import dqn2int from pyroute2.netlink import NetlinkError from pyroute2.netlink.rtnl.req import IPLinkRequest from pyroute2.netlink.rtnl.ifinfmsg import IFF_MASK from pyroute2.netlink.rtnl.ifinfmsg import ifinfmsg from pyroute2.ipdb.transactional import Transactional from pyroute2.ipdb.transactional import update from pyroute2.ipdb.linkedset import LinkedSet from pyroute2.ipdb.linkedset import IPaddrSet from pyroute2.ipdb.common import CommitException from pyroute2.ipdb.common import SYNC_TIMEOUT from pyroute2.ipdb.common import compat _BARRIER = 0.2 class Interface(Transactional): ''' Objects of this class represent network interface and all related objects: * addresses * (todo) neighbors * (todo) routes Interfaces provide transactional model and can act as context managers. Any attribute change implicitly starts a transaction. The transaction can be managed with three methods: * review() -- review changes * rollback() -- drop all the changes * commit() -- try to apply changes If anything will go wrong during transaction commit, it will be rolled back authomatically and an exception will be raised. Failed transaction review will be attached to the exception. ''' _fields_cmp = {'flags': lambda x, y: x & y & IFF_MASK == y & IFF_MASK} def __init__(self, ipdb, mode=None, parent=None, uid=None): ''' Parameters: * ipdb -- ipdb() reference * mode -- transaction mode ''' Transactional.__init__(self, ipdb, mode) self.cleanup = ('header', 'linkinfo', 'af_spec', 'attrs', 'event', 'map', 'stats', 'stats64') self.ingress = None self.egress = None self._exists = False self._flicker = False self._exception = None self._tb = None self._virtual_fields = ('removal', 'flicker', 'state') self._xfields = {'common': [ifinfmsg.nla2name(i[0]) for i in ifinfmsg.nla_map]} self._xfields['common'].append('index') self._xfields['common'].append('flags') self._xfields['common'].append('mask') self._xfields['common'].append('change') self._xfields['common'].append('kind') self._xfields['common'].append('peer') self._xfields['common'].append('vlan_id') self._xfields['common'].append('bond_mode') brmsg = ifinfmsg.ifinfo.bridge_data self._xfields['common'].extend([brmsg.nla2name(i[0]) for i in brmsg.nla_map]) bomsg = ifinfmsg.ifinfo.bond_data self._xfields['common'].extend([bomsg.nla2name(i[0]) for i in bomsg.nla_map]) tuntap = ifinfmsg.ifinfo.tuntap_data self._xfields['common'].extend([tuntap.nla2name(i[0]) for i in tuntap.nla_map]) for ftype in self._xfields: self._fields += self._xfields[ftype] self._fields.extend(self._virtual_fields) self._load_event = threading.Event() self._linked_sets.add('ipaddr') self._linked_sets.add('ports') self._freeze = None # 8<----------------------------------- # local setup: direct state is required with self._direct_state: self['ipaddr'] = IPaddrSet() self['ports'] = LinkedSet() for i in self._fields: self[i] = None for i in ('state', 'change', 'mask'): del self[i] # 8<----------------------------------- def __hash__(self): return self['index'] @property def if_master(self): ''' [property] Link to the parent interface -- if it exists ''' return self.get('master', None) def freeze(self): dump = self.dump() def cb(ipdb, msg, action): if msg.get('index', -1) == dump['index']: tr = self.load(dump) for _ in range(3): try: self.commit(transaction=tr) except (CommitException, RuntimeError): # ignore here both CommitExceptions # and RuntimeErrors (aka rollback errors), # since ususally it is a races between # 3d party setup and freeze; just # sliently try again for several times continue except NetlinkError: # on the netlink errors just give up pass break self._freeze = cb self.ipdb.register_callback(self._freeze) return self def unfreeze(self): self.ipdb.unregister_callback(self._freeze) self._freeze = None return self def load(self, data): with self._write_lock: template = self.__class__(ipdb=self.ipdb, mode='snapshot') template.load_dict(data) return template def load_dict(self, data): with self._direct_state: for key in data: if key == 'ipaddr': for addr in data[key]: if isinstance(addr, basestring): addr = (addr, ) self.add_ip(*addr) elif key == 'ports': for port in data[key]: self.add_port(port) elif key == 'neighbors': # ignore neighbors on load pass else: self[key] = data[key] def load_netlink(self, dev): ''' Update the interface info from RTM_NEWLINK message. This call always bypasses open transactions, loading changes directly into the interface data. ''' with self._direct_state: self._exists = True self.nlmsg = dev for (name, value) in dev.items(): self[name] = value for item in dev['attrs']: name, value = item[:2] norm = ifinfmsg.nla2name(name) self[norm] = value # load interface kind linkinfo = dev.get_attr('IFLA_LINKINFO') if linkinfo is not None: kind = linkinfo.get_attr('IFLA_INFO_KIND') if kind is not None: self['kind'] = kind if kind == 'vlan': data = linkinfo.get_attr('IFLA_INFO_DATA') self['vlan_id'] = data.get_attr('IFLA_VLAN_ID') # the rest is possible only when interface # is used in IPDB, not standalone if self.ipdb is not None: self['ipaddr'] = self.ipdb.ipaddr[self['index']] self['neighbors'] = self.ipdb.neighbors[self['index']] # finally, cleanup all not needed for item in self.cleanup: if item in self: del self[item] self.sync() def sync(self): self._load_event.set() @update def add_ip(self, direct, ip, mask=None, brd=None, broadcast=None): ''' Add IP address to an interface ''' # split mask if mask is None: ip, mask = ip.split('/') if mask.find('.') > -1: mask = dqn2int(mask) else: mask = int(mask, 0) brd = brd or broadcast # FIXME: make it more generic # skip IPv6 link-local addresses if ip[:4] == 'fe80' and mask == 64: return self if not direct: transaction = self.last() transaction.add_ip(ip, mask, brd) else: self['ipaddr'].unlink((ip, mask)) if brd is not None: raw = {'IFA_BROADCAST': brd} self['ipaddr'].add((ip, mask), raw=raw) else: self['ipaddr'].add((ip, mask)) return self @update def del_ip(self, direct, ip, mask=None): ''' Delete IP address from an interface ''' if mask is None: ip, mask = ip.split('/') if mask.find('.') > -1: mask = dqn2int(mask) else: mask = int(mask, 0) if not direct: transaction = self.last() if (ip, mask) in transaction['ipaddr']: transaction.del_ip(ip, mask) else: self['ipaddr'].unlink((ip, mask)) self['ipaddr'].remove((ip, mask)) return self @update def add_port(self, direct, port): ''' Add a slave port to a bridge or bonding ''' if isinstance(port, Interface): port = port['index'] if not direct: transaction = self.last() transaction.add_port(port) else: self['ports'].unlink(port) self['ports'].add(port) return self @update def del_port(self, direct, port): ''' Remove a slave port from a bridge or bonding ''' if isinstance(port, Interface): port = port['index'] if not direct: transaction = self.last() if port in transaction['ports']: transaction.del_port(port) else: self['ports'].unlink(port) self['ports'].remove(port) return self def reload(self): ''' Reload interface information ''' countdown = 3 while countdown: links = self.nl.get_links(self['index']) if links: self.load_netlink(links[0]) break else: countdown -= 1 time.sleep(1) return self def filter(self, ftype): ret = {} for key in self: if key in self._xfields[ftype]: ret[key] = self[key] return ret def commit(self, tid=None, transaction=None, rollback=False, newif=False): ''' Commit transaction. In the case of exception all changes applied during commit will be reverted. ''' error = None added = None removed = None drop = True if tid: transaction = self._transactions[tid] else: if transaction: drop = False else: transaction = self.last() wd = None with self._write_lock: # if the interface does not exist, create it first ;) if not self._exists: request = IPLinkRequest(self.filter('common')) # create watchdog wd = self.ipdb.watchdog(ifname=self['ifname']) newif = True try: # 8<---------------------------------------------------- # ACHTUNG: hack for old platforms if request.get('address', None) == '00:00:00:00:00:00': del request['address'] del request['broadcast'] # 8<---------------------------------------------------- try: self.nl.link('add', **request) except NetlinkError as x: # Operation not supported if x.code == 95 and request.get('index', 0) != 0: # ACHTUNG: hack for old platforms request = IPLinkRequest({'ifname': self['ifname'], 'kind': self['kind'], 'index': 0}) self.nl.link('add', **request) else: raise except Exception as e: # on failure, invalidate the interface and detach it # from the parent # 1. drop the IPRoute() link self.nl = None # 2. clean up ipdb self.ipdb.detach(self['index']) self.ipdb.detach(self['ifname']) # 3. invalidate the interface with self._direct_state: for i in tuple(self.keys()): del self[i] # 4. the rest self._mode = 'invalid' self._exception = e self._tb = traceback.format_exc() # raise the exception raise if wd is not None: wd.wait() # now we have our index and IP set and all other stuff snapshot = self.pick() try: removed = snapshot - transaction added = transaction - snapshot # 8<--------------------------------------------- # Interface slaves self['ports'].set_target(transaction['ports']) for i in removed['ports']: # detach the port port = self.ipdb.interfaces[i] port.set_target('master', None) port.mirror_target('master', 'link') self.nl.link('set', index=port['index'], master=0) for i in added['ports']: # enslave the port port = self.ipdb.interfaces[i] port.set_target('master', self['index']) port.mirror_target('master', 'link') self.nl.link('set', index=port['index'], master=self['index']) if removed['ports'] or added['ports']: self.nl.get_links(*(removed['ports'] | added['ports'])) self['ports'].target.wait(SYNC_TIMEOUT) if not self['ports'].target.is_set(): raise CommitException('ports target is not set') # RHEL 6.5 compat fix -- an explicit timeout # it gives a time for all the messages to pass compat.fix_timeout(1) # wait for proper targets on ports for i in list(added['ports']) + list(removed['ports']): port = self.ipdb.interfaces[i] target = port._local_targets['master'] target.wait(SYNC_TIMEOUT) del port._local_targets['master'] del port._local_targets['link'] if not target.is_set(): raise CommitException('master target failed') if i in added['ports']: assert port.if_master == self['index'] else: assert port.if_master != self['index'] # 8<--------------------------------------------- # Interface changes request = IPLinkRequest() for key in added: if key in self._xfields['common']: request[key] = added[key] request['index'] = self['index'] # apply changes only if there is something to apply if any([request[item] is not None for item in request if item != 'index']): self.nl.link('set', **request) # hardcoded pause -- if the interface was moved # across network namespaces if 'net_ns_fd' in request: time.sleep(0.5) # 8<--------------------------------------------- # IP address changes self['ipaddr'].set_target(transaction['ipaddr']) for i in removed['ipaddr']: # Ignore link-local IPv6 addresses if i[0][:4] == 'fe80' and i[1] == 64: continue # When you remove a primary IP addr, all subnetwork # can be removed. In this case you will fail, but # it is OK, no need to roll back try: self.nl.addr('delete', self['index'], i[0], i[1]) except NetlinkError as x: # bypass only errno 99, 'Cannot assign address' if x.code != 99: raise except socket.error as x: # bypass illegal IP requests if not x.args[0].startswith('illegal IP'): raise for i in added['ipaddr']: # Ignore link-local IPv6 addresses if i[0][:4] == 'fe80' and i[1] == 64: continue # Try to fetch additional address attributes try: kwarg = transaction.ipaddr[i] except KeyError: kwarg = None self.nl.addr('add', self['index'], i[0], i[1], **kwarg if kwarg else {}) # 8<-------------------------------------- # FIXME: kernel bug, sometimes `addr add` for # bond interfaces returns success, but does # really nothing if self['kind'] == 'bond': while True: try: # dirtiest hack, but we have to use it here time.sleep(0.1) self.nl.addr('add', self['index'], i[0], i[1]) # continue to try to add the address # until the kernel reports `file exists` # # a stupid solution, but must help except NetlinkError as e: if e.code == 17: break else: raise except Exception: raise # 8<-------------------------------------- if removed['ipaddr'] or added['ipaddr']: # 8<-------------------------------------- # bond and bridge interfaces do not send # IPv6 address updates, when are down # # beside of that, bridge interfaces are # down by default, so they never send # address updates from beginning # # so if we need, force address load # # FIXME: probably, we should handle other # types as well if self['kind'] in ('bond', 'bridge', 'veth'): self.nl.get_addr() # 8<-------------------------------------- self['ipaddr'].target.wait(SYNC_TIMEOUT) if not self['ipaddr'].target.is_set(): raise CommitException('ipaddr target is not set') # 8<--------------------------------------------- # reload interface to hit targets if transaction._targets: try: self.reload() except NetlinkError as e: if e.code == 19: # No such device if ('net_ns_fd' in added) or \ ('net_ns_pid' in added): # it means, that the device was moved # to another netns; just give up if drop: self.drop(transaction) return self # wait for targets transaction._wait_all_targets() # 8<--------------------------------------------- # Interface removal if added.get('removal') or \ added.get('flicker') or\ (newif and rollback): wd = self.ipdb.watchdog(action='RTM_DELLINK', ifname=self['ifname']) if added.get('flicker'): self._flicker = True self.nl.link('delete', **self) wd.wait() if added.get('flicker'): self._exists = False if added.get('removal'): self._mode = 'invalid' if drop: self.drop(transaction) return self # 8<--------------------------------------------- # Iterate callback chain for ch in self._commit_hooks: # An exception will rollback the transaction ch(self.dump(), snapshot.dump(), transaction.dump()) # 8<--------------------------------------------- except Exception as e: # something went wrong: roll the transaction back if not rollback: ret = self.commit(transaction=snapshot, rollback=True, newif=newif) # if some error was returned by the internal # closure, substitute the initial one if isinstance(ret, Exception): error = ret else: error = e error.traceback = traceback.format_exc() elif isinstance(e, NetlinkError) and \ getattr(e, 'code', 0) == 1: # It is , catched in # rollback. So return it -- see ~5 lines above e.traceback = traceback.format_exc() return e else: # somethig went wrong during automatic rollback. # that's the worst case, but it is still possible, # since we have no locks on OS level. self['ipaddr'].set_target(None) self['ports'].set_target(None) # reload all the database -- it can take a long time, # but it is required since we have no idea, what is # the result of the failure # # ACHTUNG: database reload is asynchronous, so after # getting RuntimeError() from commit(), take a seat # and rest for a while. It is an extremal case, it # should not became at all, and there is no sync. self.nl.get_links() self.nl.get_addr() x = RuntimeError() x.cause = e x.traceback = traceback.format_exc() raise x # if it is not a rollback turn if drop and not rollback: # drop last transaction in any case self.drop(transaction) # raise exception for failed transaction if error is not None: error.transaction = transaction raise error time.sleep(_BARRIER) return self def up(self): ''' Shortcut: change the interface state to 'up'. ''' if self['flags'] is None: self['flags'] = 1 else: self['flags'] |= 1 return self def down(self): ''' Shortcut: change the interface state to 'down'. ''' if self['flags'] is None: self['flags'] = 0 else: self['flags'] &= ~(self['flags'] & 1) return self def remove(self): ''' Mark the interface for removal ''' self['removal'] = True return self def shadow(self): ''' Remove the interface from the OS, but leave it in the database. When one will try to re-create interface with the same name, all the old saved attributes will apply to the new interface, incl. MAC-address and even the interface index. Please be aware, that the interface index can be reused by OS while the interface is "in the shadow state", in this case re-creation will fail. ''' self['flicker'] = True return self pyroute2-0.3.5/pyroute2/ipdb/transactional.py0000664000175000017500000003104412453333001021145 0ustar peetpeet00000000000000''' ''' import uuid import threading from pyroute2.common import Dotkeys from pyroute2.ipdb.common import SYNC_TIMEOUT from pyroute2.ipdb.common import CommitException from pyroute2.ipdb.common import DeprecationException from pyroute2.ipdb.linkedset import LinkedSet class State(object): def __init__(self, lock=None): self.lock = lock or threading.Lock() self.flag = 0 def acquire(self): self.lock.acquire() self.flag += 1 def release(self): assert self.flag > 0 self.flag -= 1 self.lock.release() def is_set(self): return self.flag def __enter__(self): self.acquire() return self def __exit__(self, exc_type, exc_value, traceback): self.release() def update(f): def decorated(self, *argv, **kwarg): # obtain update lock ret = None tid = None direct = True with self._write_lock: dcall = kwarg.pop('direct', False) if dcall: self._direct_state.acquire() direct = self._direct_state.is_set() if not direct: # 1. begin transaction for 'direct' type if self._mode == 'direct': tid = self.begin() # 2. begin transaction, if there is none elif self._mode == 'implicit': if not self._tids: self.begin() # 3. require open transaction for 'explicit' type elif self._mode == 'explicit': if not self._tids: raise TypeError('start a transaction first') # 4. transactions can not require transactions :) elif self._mode == 'snapshot': direct = True # do not support other modes else: raise TypeError('transaction mode not supported') # now that the transaction _is_ open ret = f(self, direct, *argv, **kwarg) if dcall: self._direct_state.release() if tid: # close the transaction for 'direct' type self.commit(tid) return ret decorated.__doc__ = f.__doc__ return decorated class Transactional(Dotkeys): ''' Utility class that implements common transactional logic. ''' _fields_cmp = {} def __init__(self, ipdb=None, mode=None, parent=None, uid=None): # if ipdb is not None: self.nl = ipdb.nl self.ipdb = ipdb else: self.nl = None self.ipdb = None # self._parent = None if parent is not None: self._mode = mode or parent._mode self._parent = parent elif ipdb is not None: self._mode = mode or ipdb.mode else: self._mode = mode or 'implicit' # self.nlmsg = None self.uid = uid or uuid.uuid4() self.last_error = None self._commit_hooks = [] self._fields = [] self._sids = [] self._ts = threading.local() self._snapshots = {} self._targets = {} self._local_targets = {} self._write_lock = threading.RLock() self._direct_state = State(self._write_lock) self._linked_sets = set() @property def _tids(self): if not hasattr(self._ts, 'tids'): self._ts.tids = [] return self._ts.tids @property def _transactions(self): if not hasattr(self._ts, 'transactions'): self._ts.transactions = {} return self._ts.transactions def register_callback(self, callback): raise DeprecationException("deprecated since 0.2.15;" "use `register_commit_hook()`") def register_commit_hook(self, hook): # FIXME: write docs self._commit_hooks.append(hook) def unregister_callback(self, callback): raise DeprecationException("deprecated since 0.2.15;" "use `unregister_commit_hook()`") def unregister_commit_hook(self, hook): # FIXME: write docs with self._write_lock: for cb in tuple(self._commit_hooks): if hook == cb: self._commit_hooks.pop(self._commit_hooks.index(cb)) def pick(self, detached=True, uid=None, parent=None, forge_tids=False): ''' Get a snapshot of the object. Can be of two types: * detached=True -- (default) "true" snapshot * detached=False -- keep ip addr set updated from OS Please note, that "updated" doesn't mean "in sync". The reason behind this logic is that snapshots can be used as transactions. ''' with self._write_lock: res = self.__class__(ipdb=self.ipdb, mode='snapshot', parent=parent, uid=uid) for (key, value) in self.items(): if key in self._fields: if isinstance(value, Transactional): t = value.pick(detached=detached, uid=res.uid, parent=self) if forge_tids: # forge the transaction for nested objects value._transactions[res.uid] = t value._tids.append(res.uid) res[key] = t else: res[key] = self[key] for key in self._linked_sets: res[key] = LinkedSet(self[key]) if not detached: self[key].connect(res[key]) return res def __enter__(self): # FIXME: use a bitmask? if self._mode not in ('implicit', 'explicit'): raise TypeError('context managers require a transactional mode') if not self._tids: self.begin() return self def __exit__(self, exc_type, exc_value, traceback): # apply transaction only if there was no error if exc_type is None: try: self.commit() except Exception as e: self.last_error = e raise def __repr__(self): res = {} for i in self: if self[i] is not None: res[i] = self[i] return res.__repr__() def __sub__(self, vs): res = self.__class__(ipdb=self.ipdb, mode='snapshot') with self._direct_state: # simple keys for key in self: if (key in self._fields) and \ ((key not in vs) or (self[key] != vs[key])): res[key] = self[key] for key in self._linked_sets: diff = LinkedSet(self[key] - vs[key]) if diff: res[key] = diff return res def dump(self, not_none=True): with self._write_lock: res = {} for key in self: if self[key] is not None and key[0] != '_': if isinstance(self[key], Transactional): res[key] = self[key].dump() elif isinstance(self[key], LinkedSet): res[key] = tuple(self[key]) else: res[key] = self[key] return res def load(self, data): pass def commit(self, *args, **kwarg): pass def last_snapshot_id(self): return self._sids[-1] def revert(self, sid): with self._write_lock: self._transactions[sid] = self._snapshots[sid] self._tids.append(sid) self._sids.remove(sid) del self._snapshots[sid] return self def snapshot(self): ''' Create new snapshot ''' return self._begin(mapping=self._snapshots, ids=self._sids, detached=True) def begin(self): ''' Start new transaction ''' if self._parent is not None: self._parent.begin() else: return self._begin(mapping=self._transactions, ids=self._tids, detached=False) def _begin(self, mapping, ids, detached): # keep snapshot's ip addr set updated from the OS # it is required by the commit logic if (self.ipdb is not None) and self.ipdb._stop: raise RuntimeError("Can't start transaction on released IPDB") t = self.pick(detached=detached, forge_tids=True) mapping[t.uid] = t ids.append(t.uid) return t.uid def last_snapshot(self): if not self._sids: raise TypeError('create a snapshot first') return self._snapshots[self._sids[-1]] def last(self): ''' Return last open transaction ''' with self._write_lock: if not self._tids: raise TypeError('start a transaction first') return self._transactions[self._tids[-1]] def review(self): ''' Review last open transaction ''' if not self._tids: raise TypeError('start a transaction first') with self._write_lock: added = self.last() - self removed = self - self.last() for key in self._linked_sets: added['-%s' % (key)] = removed[key] added['+%s' % (key)] = added[key] del added[key] return added def drop(self, tid=None): ''' Drop a transaction. ''' with self._write_lock: if isinstance(tid, Transactional): tid = tid.uid elif tid is None: tid = self._tids[-1] self._tids.remove(tid) del self._transactions[tid] for (key, value) in self.items(): if isinstance(value, Transactional): try: value.drop(tid) except KeyError: pass @update def __setitem__(self, direct, key, value): with self._write_lock: if not direct: # automatically set target on the last transaction, # which must be started prior to that call transaction = self.last() transaction[key] = value transaction._targets[key] = threading.Event() else: # set the item Dotkeys.__setitem__(self, key, value) # update on local targets if key in self._local_targets: func = self._fields_cmp.get(key, lambda x, y: x == y) if func(value, self._local_targets[key].value): self._local_targets[key].set() # cascade update on nested targets for tn in tuple(self._transactions.values()): if (key in tn._targets) and (key in tn): if self._fields_cmp.\ get(key, lambda x, y: x == y)(value, tn[key]): tn._targets[key].set() @update def __delitem__(self, direct, key): with self._write_lock: # firstly set targets self[key] = None # then continue with delete if not direct: transaction = self.last() if key in transaction: del transaction[key] else: Dotkeys.__delitem__(self, key) def option(self, key, value): self[key] = value return self def unset(self, key): del self[key] return self def _wait_all_targets(self): for key, target in self._targets.items(): if key not in self._virtual_fields: target.wait(SYNC_TIMEOUT) if not target.is_set(): raise CommitException('target %s is not set' % key) def set_target(self, key, value): self._local_targets[key] = threading.Event() self._local_targets[key].value = value def mirror_target(self, key_from, key_to): self._local_targets[key_to] = self._local_targets[key_from] def set_item(self, key, value): with self._direct_state: self[key] = value def del_item(self, key): with self._direct_state: del self[key] pyroute2-0.3.5/pyroute2/protocols.py0000664000175000017500000001006412432725447017430 0ustar peetpeet00000000000000# # IEEE = 802.3 Ethernet magic constants. The frame sizes omit # the preamble and FCS/CRC (frame check sequence). # ETH_ALEN = 6 # Octets in one ethernet addr ETH_HLEN = 14 # Total octets in header. ETH_ZLEN = 60 # Min. octets in frame sans FCS ETH_DATA_LEN = 1500 # Max. octets in payload ETH_FRAME_LEN = 1514 # Max. octets in frame sans FCS ETH_FCS_LEN = 4 # Octets in the FCS # # These are the defined Ethernet Protocol ID's. # ETH_P_LOOP = 0x0060 # Ethernet Loopback packet ETH_P_PUP = 0x0200 # Xerox PUP packet ETH_P_PUPAT = 0x0201 # Xerox PUP Addr Trans packet ETH_P_IP = 0x0800 # Internet Protocol packet ETH_P_X25 = 0x0805 # CCITT X.25 ETH_P_ARP = 0x0806 # Address Resolution packet ETH_P_BPQ = 0x08FF # G8BPQ AX.25 Ethernet Packet # ^^^ [ NOT AN OFFICIALLY REGISTERED ID ] ETH_P_IEEEPUP = 0x0a00 # Xerox IEEE802.3 PUP packet ETH_P_IEEEPUPAT = 0x0a01 # Xerox IEEE802.3 PUP Addr Trans packet ETH_P_DEC = 0x6000 # DEC Assigned proto ETH_P_DNA_DL = 0x6001 # DEC DNA Dump/Load ETH_P_DNA_RC = 0x6002 # DEC DNA Remote Console ETH_P_DNA_RT = 0x6003 # DEC DNA Routing ETH_P_LAT = 0x6004 # DEC LAT ETH_P_DIAG = 0x6005 # DEC Diagnostics ETH_P_CUST = 0x6006 # DEC Customer use ETH_P_SCA = 0x6007 # DEC Systems Comms Arch ETH_P_TEB = 0x6558 # Trans Ether Bridging ETH_P_RARP = 0x8035 # Reverse Addr Res packet ETH_P_ATALK = 0x809B # Appletalk DDP ETH_P_AARP = 0x80F3 # Appletalk AARP ETH_P_8021Q = 0x8100 # = 802.1Q VLAN Extended Header ETH_P_IPX = 0x8137 # IPX over DIX ETH_P_IPV6 = 0x86DD # IPv6 over bluebook ETH_P_PAUSE = 0x8808 # IEEE Pause frames. See = 802.3 = 31B ETH_P_SLOW = 0x8809 # Slow Protocol. See = 802.3ad = 43B ETH_P_WCCP = 0x883E # Web-cache coordination protocol # defined in draft-wilson-wrec-wccp-v2-00.txt ETH_P_PPP_DISC = 0x8863 # PPPoE discovery messages ETH_P_PPP_SES = 0x8864 # PPPoE session messages ETH_P_MPLS_UC = 0x8847 # MPLS Unicast traffic ETH_P_MPLS_MC = 0x8848 # MPLS Multicast traffic ETH_P_ATMMPOA = 0x884c # MultiProtocol Over ATM ETH_P_LINK_CTL = 0x886c # HPNA, wlan link local tunnel ETH_P_ATMFATE = 0x8884 # Frame-based ATM Transport over Ethernet ETH_P_PAE = 0x888E # Port Access Entity (IEEE = 802.1X) ETH_P_AOE = 0x88A2 # ATA over Ethernet ETH_P_8021AD = 0x88A8 # = 802.1ad Service VLAN ETH_P_802_EX1 = 0x88B5 # = 802.1 Local Experimental = 1. ETH_P_TIPC = 0x88CA # TIPC ETH_P_8021AH = 0x88E7 # = 802.1ah Backbone Service Tag ETH_P_1588 = 0x88F7 # IEEE = 1588 Timesync ETH_P_FCOE = 0x8906 # Fibre Channel over Ethernet ETH_P_TDLS = 0x890D # TDLS ETH_P_FIP = 0x8914 # FCoE Initialization Protocol ETH_P_QINQ1 = 0x9100 # deprecated QinQ VLAN # ^^^ [ NOT AN OFFICIALLY REGISTERED ID ] ETH_P_QINQ2 = 0x9200 # deprecated QinQ VLAN # ^^^ [ NOT AN OFFICIALLY REGISTERED ID ] ETH_P_QINQ3 = 0x9300 # deprecated QinQ VLAN # ^^^ [ NOT AN OFFICIALLY REGISTERED ID ] ETH_P_EDSA = 0xDADA # Ethertype DSA # ^^^ [ NOT AN OFFICIALLY REGISTERED ID ] ETH_P_AF_IUCV = 0xFBFB # IBM af_iucv # ^^^ [ NOT AN OFFICIALLY REGISTERED ID ] # # Non DIX types. Won't clash for = 1500 types. # ETH_P_802_3 = 0x0001 # Dummy type for = 802.3 frames ETH_P_AX25 = 0x0002 # Dummy protocol id for AX.25 ETH_P_ALL = 0x0003 # Every packet (be careful!!!) ETH_P_802_2 = 0x0004 # = 802.2 frames ETH_P_SNAP = 0x0005 # Internal only ETH_P_DDCMP = 0x0006 # DEC DDCMP: Internal only ETH_P_WAN_PPP = 0x0007 # Dummy type for WAN PPP frames*/ ETH_P_PPP_MP = 0x0008 # Dummy type for PPP MP frames ETH_P_LOCALTALK = 0x0009 # Localtalk pseudo type ETH_P_CAN = 0x000C # Controller Area Network ETH_P_PPPTALK = 0x0010 # Dummy type for Atalk over PPP*/ ETH_P_TR_802_2 = 0x0011 # = 802.2 frames ETH_P_MOBITEX = 0x0015 # Mobitex (kaz@cafe.net) ETH_P_CONTROL = 0x0016 # Card specific control frames ETH_P_IRDA = 0x0017 # Linux-IrDA ETH_P_ECONET = 0x0018 # Acorn Econet ETH_P_HDLC = 0x0019 # HDLC frames ETH_P_ARCNET = 0x001A # = 1A for ArcNet :-) ETH_P_DSA = 0x001B # Distributed Switch Arch. ETH_P_TRAILER = 0x001C # Trailer switch tagging ETH_P_PHONET = 0x00F5 # Nokia Phonet frames ETH_P_IEEE802154 = 0x00F6 # IEEE802.15.4 frame ETH_P_CAIF = 0x00F7 # ST-Ericsson CAIF protocol pyroute2-0.3.5/pyroute2/__init__.py0000664000175000017500000000174412471077567017156 0ustar peetpeet00000000000000## # defer all root imports # # this allows to safely import config, change it, and # only after that import modules itself # # surely, you can also import modules directly from their # places ## from functools import partial __all__ = [] _modules = {'IPRoute': 'pyroute2.iproute', 'IPDB': 'pyroute2.ipdb', 'IW': 'pyroute2.iwutil', 'NetNS': 'pyroute2.netns', 'IPRSocket': 'pyroute2.netlink.rtnl', 'TaskStats': 'pyroute2.netlink.taskstats', 'NL80211': 'pyroute2.netlink.nl80211', 'IPQSocket': 'pyroute2.netlink.ipq', 'GenericNetlinkSocket': 'pyroute2.netlink.generic', 'NetlinkError': 'pyroute2.netlink'} def _wrapper(name, *argv, **kwarg): _temp = __import__(_modules[name], globals(), locals(), [name], 0) return getattr(_temp, name)(*argv, **kwarg) for name in _modules: f = partial(_wrapper, name) f.__name__ = name globals()[name] = f __all__.append(name) pyroute2-0.3.5/pyroute2/proxy.py0000664000175000017500000000350312472732376016570 0ustar peetpeet00000000000000''' Netlink proxy engine ''' import errno import struct import logging import traceback import threading class NetlinkProxy(object): ''' Proxy schemes:: User -> NetlinkProxy -> Kernel | <---------+ User <- NetlinkProxy <- Kernel ''' def __init__(self, policy='forward', nl=None, lock=None): self.nl = nl self.lock = lock or threading.Lock() self.pmap = {} self.policy = policy def handle(self, data): # # match the packet # ptype = struct.unpack('H', data[4:6])[0] plugin = self.pmap.get(ptype, None) if plugin is not None: with self.lock: try: ret = plugin(data, self.nl) if ret is None: msg = struct.pack('IHH', 40, 2, 0) msg += data[8:16] msg += struct.pack('I', 0) # nlmsgerr struct alignment msg += b'\0' * 20 return {'verdict': self.policy, 'data': msg} else: return ret except Exception as e: logging.error(traceback.format_exc()) # errmsg if isinstance(e, (OSError, IOError)): code = e.errno else: code = errno.ECOMM msg = struct.pack('HH', 2, 0) msg += data[8:16] msg += struct.pack('I', code) msg += data msg = struct.pack('I', len(msg) + 4) + msg return {'verdict': 'error', 'data': msg} return None pyroute2-0.3.5/pyroute2/common.py0000664000175000017500000001572312467731656016712 0ustar peetpeet00000000000000''' Common utilities ''' import re import os import sys import struct import platform import threading from socket import inet_aton try: basestring = basestring except NameError: basestring = (str, bytes) AF_PIPE = 255 # Right now AF_MAX == 40 DEFAULT_RCVBUF = 16384 ANCIENT = (platform.dist()[0] in ('redhat', 'centos') and platform.dist()[1].startswith('6.') or os.environ.get('PYROUTE2_ANCIENT', False)) size_suffixes = {'b': 1, 'k': 1024, 'kb': 1024, 'm': 1024 * 1024, 'mb': 1024 * 1024, 'g': 1024 * 1024 * 1024, 'gb': 1024 * 1024 * 1024, 'kbit': 1024 / 8, 'mbit': 1024 * 1024 / 8, 'gbit': 1024 * 1024 * 1024 / 8} time_suffixes = {'s': 1, 'sec': 1, 'secs': 1, 'ms': 1000, 'msec': 1000, 'msecs': 1000, 'us': 1000000, 'usec': 1000000, 'usecs': 1000000} rate_suffixes = {'bit': 1, 'Kibit': 1024, 'kbit': 1000, 'mibit': 1024 * 1024, 'mbit': 1000000, 'gibit': 1024 * 1024 * 1024, 'gbit': 1000000000, 'tibit': 1024 * 1024 * 1024 * 1024, 'tbit': 1000000000000, 'Bps': 8, 'KiBps': 8 * 1024, 'KBps': 8000, 'MiBps': 8 * 1024 * 1024, 'MBps': 8000000, 'GiBps': 8 * 1024 * 1024 * 1024, 'GBps': 8000000000, 'TiBps': 8 * 1024 * 1024 * 1024 * 1024, 'TBps': 8000000000000} ## # General purpose # class Dotkeys(dict): ''' This is a sick-minded hack of dict, intended to be an eye-candy. It allows to get dict's items byt dot reference: ipdb["lo"] == ipdb.lo ipdb["eth0"] == ipdb.eth0 Obviously, it will not work for some cases, like unicode names of interfaces and so on. Beside of that, it introduces some complexity. But it simplifies live for old-school admins, who works with good old "lo", "eth0", and like that naming schemes. ''' var_name = re.compile('^[a-zA-Z_]+[a-zA-Z_0-9]*$') def __dir__(self): return [i for i in self if type(i) == str and self.var_name.match(i)] def __getattribute__(self, key, *argv): try: return dict.__getattribute__(self, key) except AttributeError as e: if key == '__deepcopy__': raise e return self[key] def __setattr__(self, key, value): if key in self: self[key] = value else: dict.__setattr__(self, key, value) def __delattr__(self, key): if key in self: del self[key] else: dict.__delattr__(self, key) def map_namespace(prefix, ns): ''' Take the namespace prefix, list all constants and build two dictionaries -- straight and reverse mappings. E.g.: ## neighbor attributes NDA_UNSPEC = 0 NDA_DST = 1 NDA_LLADDR = 2 NDA_CACHEINFO = 3 NDA_PROBES = 4 (NDA_NAMES, NDA_VALUES) = map_namespace('NDA', globals()) Will lead to: NDA_NAMES = {'NDA_UNSPEC': 0, ... 'NDA_PROBES': 4} NDA_VALUES = {0: 'NDA_UNSPEC', ... 4: 'NDA_PROBES'} ''' by_name = dict([(i, ns[i]) for i in ns.keys() if i.startswith(prefix)]) by_value = dict([(ns[i], i) for i in ns.keys() if i.startswith(prefix)]) return (by_name, by_value) def dqn2int(mask): ''' IPv4 dotted quad notation to int mask conversion ''' return bin(struct.unpack('>L', inet_aton(mask))[0]).count('1') def hexdump(payload, length=0): ''' Represent byte string as hex -- for debug purposes ''' if sys.version[0] == '3': return ':'.join('{0:02x}'.format(c) for c in payload[:length] or payload) else: return ':'.join('{0:02x}'.format(ord(c)) for c in payload[:length] or payload) class AddrPool(object): ''' Address pool ''' cell = 0xffffffffffffffff def __init__(self, minaddr=0xf, maxaddr=0xffffff, reverse=False): self.cell_size = 0 # in bits mx = self.cell self.reverse = reverse self.ban = [] while mx: mx >>= 8 self.cell_size += 1 self.cell_size *= 8 # calculate, how many ints we need to bitmap all addresses self.cells = int((maxaddr - minaddr) / self.cell_size + 1) # initial array self.addr_map = [self.cell] self.minaddr = minaddr self.maxaddr = maxaddr self.lock = threading.RLock() def alloc(self): with self.lock: # gc self.ban: for item in tuple(self.ban): if item['counter'] == 0: self.free(item['addr']) self.ban.remove(item) else: item['counter'] -= 1 # iterate through addr_map base = 0 for cell in self.addr_map: if cell: # not allocated addr bit = 0 while True: if (1 << bit) & self.addr_map[base]: self.addr_map[base] ^= 1 << bit break bit += 1 ret = (base * self.cell_size + bit) if self.reverse: ret = self.maxaddr - ret else: ret = ret + self.minaddr if self.minaddr <= ret <= self.maxaddr: return ret else: self.free(ret) raise KeyError('no free address available') base += 1 # no free address available if len(self.addr_map) < self.cells: # create new cell to allocate address from self.addr_map.append(self.cell) return self.alloc() else: raise KeyError('no free address available') def free(self, addr, ban=0): with self.lock: if ban != 0: self.ban.append({'addr': addr, 'counter': ban}) else: if self.reverse: addr = self.maxaddr - addr else: addr -= self.minaddr base = addr // self.cell_size bit = addr % self.cell_size if len(self.addr_map) <= base: raise KeyError('address is not allocated') if self.addr_map[base] & (1 << bit): raise KeyError('address is not allocated') self.addr_map[base] ^= 1 << bit pyroute2-0.3.5/pyroute2/iwutil.py0000664000175000017500000000760512474416360016725 0ustar peetpeet00000000000000''' IW module ========= Experimental module ''' from pyroute2.netlink import NLM_F_REQUEST from pyroute2.netlink import NLM_F_DUMP from pyroute2.netlink.nl80211 import NL80211 from pyroute2.netlink.nl80211 import nl80211cmd from pyroute2.netlink.nl80211 import NL80211_NAMES class IW(NL80211): def __init__(self, *argv, **kwarg): # get specific groups kwarg if 'groups' in kwarg: groups = kwarg['groups'] del kwarg['groups'] else: groups = None # get specific async kwarg if 'async' in kwarg: async = kwarg['async'] del kwarg['async'] else: async = False # align groups with async if groups is None: groups = ~0 if async else 0 # continue with init super(IW, self).__init__(*argv, **kwarg) # do automatic bind # FIXME: unfortunately we can not omit it here self.bind(groups, async) def list_wiphy(self): ''' Get all list of phy device ''' msg = nl80211cmd() msg['cmd'] = NL80211_NAMES['NL80211_CMD_GET_WIPHY'] return self.nlm_request(msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_DUMP) def get_interface_by_phy(self, attr): ''' Get interface by phy name ( use x.get_attr('NL80211_ATTR_WIPHY') ) ''' msg = nl80211cmd() msg['cmd'] = NL80211_NAMES['NL80211_CMD_GET_INTERFACE'] msg['attrs'] = [['NL80211_ATTR_WIPHY', attr]] return self.nlm_request(msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_DUMP) def get_interface_by_ifindex(self, ifindex): ''' Get interface by ifindex ( use x.get_attr('NL80211_ATTR_IFINDEX') ''' msg = nl80211cmd() msg['cmd'] = NL80211_NAMES['NL80211_CMD_GET_INTERFACE'] msg['attrs'] = [['NL80211_ATTR_IFINDEX', ifindex]] return self.nlm_request(msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST) def connect(self, ifindex, ssid, bssid=None): ''' Connect to the ap with ssid and bssid Warn: Use of put because message does return nothing, Use this function with the good right (Root or may be setcap ) ''' msg = nl80211cmd() msg['cmd'] = NL80211_NAMES['NL80211_CMD_CONNECT'] msg['attrs'] = [['NL80211_ATTR_IFINDEX', ifindex], ['NL80211_ATTR_SSID', ssid]] if bssid is not None: msg['attrs'].append(['NL80211_ATTR_MAC', bssid]) self.put(msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST) def disconnect(self, ifindex): ''' Disconnect the device ''' msg = nl80211cmd() msg['cmd'] = NL80211_NAMES['NL80211_CMD_DISCONNECT'] msg['attrs'] = [['NL80211_ATTR_IFINDEX', ifindex]] self.put(msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST) def scan(self, ifindex): ''' Scan wifi ''' msg = nl80211cmd() msg['cmd'] = NL80211_NAMES['NL80211_CMD_TRIGGER_SCAN'] msg['attrs'] = [['NL80211_ATTR_IFINDEX', ifindex]] self.put(msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST) scanResultNotFound = True while scanResultNotFound: listMsg = self.get() for msg in listMsg: if msg["event"] == "NL80211_CMD_NEW_SCAN_RESULTS": scanResultNotFound = False break msg2 = nl80211cmd() msg2['cmd'] = NL80211_NAMES['NL80211_CMD_GET_SCAN'] msg2['attrs'] = [['NL80211_ATTR_IFINDEX', ifindex]] return self.nlm_request(msg2, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_DUMP) pyroute2-0.3.5/pyroute2/netns.py0000664000175000017500000003063412471077567016546 0ustar peetpeet00000000000000''' NetNS, network namespaces support ================================= Pyroute2 provides basic network namespaces support. The core class is `NetNS`. Please be aware, that in order to run system calls the library uses `ctypes` module. It can fail on platforms where SELinux is enforced. If the Python interpreter, loading this module, dumps the core, one can check the SELinux state with `getenforce` command. By default, NetNS creates requested netns, if it doesn't exist, or uses existing one. To control this behaviour, one can use flags as for `open(2)` system call:: # create a new netns or fail, if it already exists netns = NetNS('test', flags=os.O_CREAT | os.O_EXIST) # create a new netns or use existing one netns = NetNS('test', flags=os.O_CREAT) # the same as above, the default behaviour netns = NetNS('test') NetNS supports standard IPRoute API, so can be used instead of IPRoute, e.g., in IPDB:: # start the main network settings database: ipdb_main = IPDB() # start the same for a netns: ipdb_test = IPDB(nl=NetNS('test')) # create VETH ipdb_main.create(ifname='v0p0', kind='veth', peer='v0p1').commit() # move peer VETH into the netns with ipdb_main.interfaces.v0p1 as veth: veth.net_ns_fd = 'test' # please keep in mind, that netns move clears all the settings # on a VETH interface pair, so one should run netns assignment # as a separate operation only # assign addresses # please notice, that `v0p1` is already in the `test` netns, # so should be accessed via `ipdb_test` with ipdb_main.interfaces.v0p0 as veth: veth.add_ip('172.16.200.1/24') veth.up() with ipdb_test.interfaces.v0p1 as veth: veth.add_ip('172.16.200.2/24') veth.up() Please review also the test code, under `tests/test_netns.py` for more examples. To remove a network namespace, one can use one of two ways:: # The approach 1) # from pyroute2 import NetNS netns = NetNS('test') netns.close() netns.remove() # The approach 2) # from pyroute2.netns import remove remove('test') Using NetNS, one should stop it first with `close()`, and only after that run `remove()`. classes and functions --------------------- ''' import os import errno import atexit import ctypes import select import struct import threading import traceback from socket import SOL_SOCKET from socket import SO_RCVBUF from pyroute2.config import MpPipe from pyroute2.config import MpProcess from pyroute2.iproute import IPRoute from pyroute2.netlink.nlsocket import NetlinkMixin from pyroute2.netlink.rtnl import IPRSocketMixin from pyroute2.iproute import IPRouteMixin __NR_setns = 308 # FIXME CLONE_NEWNET = 0x40000000 MNT_DETACH = 0x00000002 MS_BIND = 4096 MS_REC = 16384 MS_SHARED = 1 << 20 NETNS_RUN_DIR = '/var/run/netns' def listnetns(): ''' List available netns. ''' try: return os.listdir(NETNS_RUN_DIR) except OSError as e: if e.errno == errno.ENOENT: return [] else: raise def create(netns, libc=None): ''' Create a network namespace. ''' libc = libc or ctypes.CDLL('libc.so.6') # FIXME validate and prepare NETNS_RUN_DIR netnspath = '%s/%s' % (NETNS_RUN_DIR, netns) netnspath = netnspath.encode('ascii') netnsdir = NETNS_RUN_DIR.encode('ascii') # init netnsdir try: os.mkdir(netnsdir) except OSError as e: if e.errno != errno.EEXIST: raise # this code is ported from iproute2 done = False while libc.mount(b'', netnsdir, b'none', MS_SHARED | MS_REC, None) != 0: if done: raise OSError(errno.ECOMM, 'share rundir failed', netns) if libc.mount(netnsdir, netnsdir, b'none', MS_BIND, None) != 0: raise OSError(errno.ECOMM, 'mount rundir failed', netns) done = True # create mountpoint os.close(os.open(netnspath, os.O_RDONLY | os.O_CREAT | os.O_EXCL, 0)) # unshare if libc.unshare(CLONE_NEWNET) < 0: raise OSError(errno.ECOMM, 'unshare failed', netns) # bind the namespace if libc.mount(b'/proc/self/ns/net', netnspath, b'none', MS_BIND, None) < 0: raise OSError(errno.ECOMM, 'mount failed', netns) def remove(netns, libc=None): ''' Remove a network namespace. ''' libc = libc or ctypes.CDLL('libc.so.6') netnspath = '%s/%s' % (NETNS_RUN_DIR, netns) netnspath = netnspath.encode('ascii') libc.umount2(netnspath, MNT_DETACH) os.unlink(netnspath) def setns(netns, flags=os.O_CREAT, libc=None): ''' Set netns for the current process. ''' libc = libc or ctypes.CDLL('libc.so.6') netnspath = '%s/%s' % (NETNS_RUN_DIR, netns) netnspath = netnspath.encode('ascii') if netns in listnetns(): if flags & (os.O_CREAT | os.O_EXCL) == (os.O_CREAT | os.O_EXCL): raise OSError(errno.EEXIST, 'netns exists', netns) else: if flags & os.O_CREAT: create(netns, libc=libc) nsfd = os.open(netnspath, os.O_RDONLY) ret = libc.syscall(__NR_setns, nsfd, CLONE_NEWNET) if ret != 0: raise OSError(ret, 'failed to open netns', netns) return nsfd def NetNServer(netns, rcvch, cmdch, flags=os.O_CREAT): ''' The netns server supposed to be started automatically by NetNS. It has two communication channels: one simplex to forward incoming netlink packets, `rcvch`, and other synchronous duplex to get commands and send back responses, `cmdch`. Channels should support standard socket API, should be compatible with poll/select and should be able to transparently pickle objects. NetNS uses `multiprocessing.Pipe` for this purpose, but it can be any other implementation with compatible API. The first parameter, `netns`, is a netns name. Depending on the `flags`, the netns can be created automatically. The `flags` semantics is exactly the same as for `open(2)` system call. ... The server workflow is simple. The startup sequence:: 1. Create or open a netns. 2. Start `IPRoute` instance. It will be used only on the low level, the `IPRoute` will not parse any packet. 3. Start poll/select loop on `cmdch` and `IPRoute`. On the startup, the server sends via `cmdch` the status packet. It can be `None` if all is OK, or some exception. Further data handling, depending on the channel, server side:: 1. `IPRoute`: read an incoming netlink packet and send it unmodified to the peer via `rcvch`. The peer, polling `rcvch`, can handle the packet on its side. 2. `cmdch`: read tuple (cmd, argv, kwarg). If the `cmd` starts with "send", then take `argv[0]` as a packet buffer, treat it as one netlink packet and substitute PID field (offset 12, uint32) with its own. Strictly speaking, it is not mandatory for modern netlink implementations, but it is required by the protocol standard. ''' try: nsfd = setns(netns, flags) except OSError as e: cmdch.send(e) return e.errno except Exception as e: cmdch.send(OSError(errno.ECOMM, str(e), netns)) return 255 # try: ipr = IPRoute() rcvch_lock = ipr._sproxy.lock ipr._s_channel = rcvch poll = select.poll() poll.register(ipr, select.POLLIN | select.POLLPRI) poll.register(cmdch, select.POLLIN | select.POLLPRI) except Exception as e: cmdch.send(e) return 255 # all is OK so far cmdch.send(None) # 8<------------------------------------------------------------- while True: events = poll.poll() for (fd, event) in events: if fd == ipr.fileno(): bufsize = ipr.getsockopt(SOL_SOCKET, SO_RCVBUF) // 2 with rcvch_lock: rcvch.send(ipr.recv(bufsize)) elif fd == cmdch.fileno(): try: cmdline = cmdch.recv() if cmdline is None: poll.unregister(ipr) poll.unregister(cmdch) ipr.close() os.close(nsfd) return (cmd, argv, kwarg) = cmdline if cmd[:4] == 'send': # Achtung # # It's a hack, but we just have to do it: one # must use actual pid in netlink messages # # FIXME: there can be several messages in one # call buffer; but right now we can ignore it msg = argv[0][:12] msg += struct.pack("I", os.getpid()) msg += argv[0][16:] argv = list(argv) argv[0] = msg cmdch.send(getattr(ipr, cmd)(*argv, **kwarg)) except Exception as e: e.tb = traceback.format_exc() cmdch.send(e) class NetNSProxy(object): netns = 'default' flags = os.O_CREAT def __init__(self, *argv, **kwarg): self.cmdlock = threading.Lock() self.rcvch, rcvch = MpPipe() self.cmdch, cmdch = MpPipe() self.server = MpProcess(target=NetNServer, args=(self.netns, rcvch, cmdch, self.flags)) self.server.start() error = self.cmdch.recv() if error is not None: self.server.join() raise error else: atexit.register(self.close) def recv(self, bufsize, flags=0): return self.rcvch.recv() def close(self): self.cmdch.send(None) self.server.join() def proxy(self, cmd, *argv, **kwarg): with self.cmdlock: self.cmdch.send((cmd, argv, kwarg)) response = self.cmdch.recv() if isinstance(response, Exception): raise response return response def fileno(self): return self.rcvch.fileno() def bind(self, *argv, **kwarg): if 'async' in kwarg: kwarg['async'] = False return self.proxy('bind', *argv, **kwarg) def send(self, *argv, **kwarg): return self.proxy('send', *argv, **kwarg) def sendto(self, *argv, **kwarg): return self.proxy('sendto', *argv, **kwarg) def getsockopt(self, *argv, **kwarg): return self.proxy('getsockopt', *argv, **kwarg) def setsockopt(self, *argv, **kwarg): return self.proxy('setsockopt', *argv, **kwarg) class NetNSocket(NetlinkMixin, NetNSProxy): def bind(self, *argv, **kwarg): return NetNSProxy.bind(self, *argv, **kwarg) class NetNSIPR(IPRSocketMixin, NetNSocket): pass class NetNS(IPRouteMixin, NetNSIPR): ''' NetNS is the IPRoute API with network namespace support. **Why not IPRoute?** The task to run netlink commands in some network namespace, being in another network namespace, requires the architecture, that differs too much from a simple Netlink socket. NetNS starts a proxy process in a network namespace and uses `multiprocessing` communication channels between the main and the proxy processes to route all `recv()` and `sendto()` requests/responses. **Any specific API calls?** Nope. `NetNS` supports all the same, that `IPRoute` does, in the same way. It provides full `socket`-compatible API and can be used in poll/select as well. The only difference is the `close()` call. In the case of `NetNS` it is **mandatory** to close the socket before exit. **NetNS and IPDB** It is possible to run IPDB with NetNS:: from pyroute2 import NetNS from pyroute2 import IPDB ip = IPDB(nl=NetNS('somenetns')) ... ip.release() Do not forget to call `release()` when the work is done. It will shut down `NetNS` instance as well. ''' def __init__(self, netns, flags=os.O_CREAT): self.netns = netns self.flags = flags super(NetNS, self).__init__() # disconnect proxy services self.sendto = self._sendto self.recv = self._recv self._sproxy = None self._rproxy = None def remove(self): ''' Try to remove this network namespace from the system. This call be be ran only after `NetNS.close()`, otherwise it will fail. ''' remove(self.netns) pyroute2-0.3.5/pyroute2/config.py0000664000175000017500000000020312471437136016641 0ustar peetpeet00000000000000import socket import multiprocessing SocketBase = socket.socket MpPipe = multiprocessing.Pipe MpProcess = multiprocessing.Process pyroute2-0.3.5/pyroute2/arp.py0000664000175000017500000000501412432725447016165 0ustar peetpeet00000000000000from pyroute2.common import map_namespace # ARP protocol HARDWARE identifiers. ARPHRD_NETROM = 0 # from KA9Q: NET/ROM pseudo ARPHRD_ETHER = 1 # Ethernet 10Mbps ARPHRD_EETHER = 2 # Experimental Ethernet ARPHRD_AX25 = 3 # AX.25 Level 2 ARPHRD_PRONET = 4 # PROnet token ring ARPHRD_CHAOS = 5 # Chaosnet ARPHRD_IEEE802 = 6 # IEEE 802.2 Ethernet/TR/TB ARPHRD_ARCNET = 7 # ARCnet ARPHRD_APPLETLK = 8 # APPLEtalk ARPHRD_DLCI = 15 # Frame Relay DLCI ARPHRD_ATM = 19 # ATM ARPHRD_METRICOM = 23 # Metricom STRIP (new IANA id) ARPHRD_IEEE1394 = 24 # IEEE 1394 IPv4 - RFC 2734 ARPHRD_EUI64 = 27 # EUI-64 ARPHRD_INFINIBAND = 32 # InfiniBand # Dummy types for non ARP hardware ARPHRD_SLIP = 256 ARPHRD_CSLIP = 257 ARPHRD_SLIP6 = 258 ARPHRD_CSLIP6 = 259 ARPHRD_RSRVD = 260 # Notional KISS type ARPHRD_ADAPT = 264 ARPHRD_ROSE = 270 ARPHRD_X25 = 271 # CCITT X.25 ARPHRD_HWX25 = 272 # Boards with X.25 in firmware ARPHRD_PPP = 512 ARPHRD_CISCO = 513 # Cisco HDLC ARPHRD_HDLC = ARPHRD_CISCO ARPHRD_LAPB = 516 # LAPB ARPHRD_DDCMP = 517 # Digital's DDCMP protocol ARPHRD_RAWHDLC = 518 # Raw HDLC ARPHRD_TUNNEL = 768 # IPIP tunnel ARPHRD_TUNNEL6 = 769 # IP6IP6 tunnel ARPHRD_FRAD = 770 # Frame Relay Access Device ARPHRD_SKIP = 771 # SKIP vif ARPHRD_LOOPBACK = 772 # Loopback device ARPHRD_LOCALTLK = 773 # Localtalk device ARPHRD_FDDI = 774 # Fiber Distributed Data Interface ARPHRD_BIF = 775 # AP1000 BIF ARPHRD_SIT = 776 # sit0 device - IPv6-in-IPv4 ARPHRD_IPDDP = 777 # IP over DDP tunneller ARPHRD_IPGRE = 778 # GRE over IP ARPHRD_PIMREG = 779 # PIMSM register interface ARPHRD_HIPPI = 780 # High Performance Parallel Interface ARPHRD_ASH = 781 # Nexus 64Mbps Ash ARPHRD_ECONET = 782 # Acorn Econet ARPHRD_IRDA = 783 # Linux-IrDA # ARP works differently on different FC media .. so ARPHRD_FCPP = 784 # Point to point fibrechannel ARPHRD_FCAL = 785 # Fibrechannel arbitrated loop ARPHRD_FCPL = 786 # Fibrechannel public loop ARPHRD_FCFABRIC = 787 # Fibrechannel fabric # 787->799 reserved for fibrechannel media types ARPHRD_IEEE802_TR = 800 # Magic type ident for TR ARPHRD_IEEE80211 = 801 # IEEE 802.11 ARPHRD_IEEE80211_PRISM = 802 # IEEE 802.11 + Prism2 header ARPHRD_IEEE80211_RADIOTAP = 803 # IEEE 802.11 + radiotap header ARPHRD_MPLS_TUNNEL = 899 # MPLS Tunnel Interface ARPHRD_VOID = 0xFFFF # Void type, nothing is known ARPHRD_NONE = 0xFFFE # zero header length (ARPHRD_NAMES, ARPHRD_VALUES) = map_namespace("ARPHRD_", globals()) pyroute2-0.3.5/pyroute2/iproute.py0000664000175000017500000007356212471077567017115 0ustar peetpeet00000000000000''' IPRoute module ============== iproute quickstart ------------------ **IPRoute** in two words:: $ sudo pip install pyroute2 $ cat example.py from pyroute2 import IPRoute ip = IPRoute() print([x.get_attr('IFLA_IFNAME') for x in ip.get_links()]) $ python example.py ['lo', 'p6p1', 'wlan0', 'virbr0', 'virbr0-nic'] threaded vs. threadless architecture ------------------------------------ Since v0.3.2, IPRoute class is threadless by default. It spawns no additional threads, and receives only responses to own requests, no broadcast messages. So, if you prefer not to cope with implicit threading, you can safely use this module. To get broadcast messages, use `IPRoute.bind()` call. Please notice, that after calling `IPRoute.bind()` you MUST get all the messages in time. In the case of the kernel buffer overflow, you will have to restart the socket. With `IPRoute.bind(async=True)` one can launch async message receiver thread with `Queue`-based buffer. The buffer is thread-safe and completely transparent from the programmer's perspective. Please read also `NetlinkSocket` documentation to know more about async mode. think about IPDB ---------------- If you plan to regularly fetch loads of objects, think about IPDB also. Unlike to IPRoute, IPDB does not fetch all the objects from OS every time you request them, but keeps a cache that is asynchronously updated by the netlink broadcasts. For a long-term running programs, that often retrieve info about hundreds or thousands of objects, it can be better to use IPDB as it will load CPU significantly less. classes ------- ''' from socket import htons from socket import AF_INET from socket import AF_INET6 from socket import AF_UNSPEC from pyroute2.netlink import NLMSG_ERROR from pyroute2.netlink import NLM_F_ATOMIC from pyroute2.netlink import NLM_F_ROOT from pyroute2.netlink import NLM_F_REPLACE from pyroute2.netlink import NLM_F_REQUEST from pyroute2.netlink import NLM_F_ACK from pyroute2.netlink import NLM_F_DUMP from pyroute2.netlink import NLM_F_CREATE from pyroute2.netlink import NLM_F_EXCL from pyroute2.netlink.rtnl import RTM_NEWADDR from pyroute2.netlink.rtnl import RTM_GETADDR from pyroute2.netlink.rtnl import RTM_DELADDR from pyroute2.netlink.rtnl import RTM_NEWLINK from pyroute2.netlink.rtnl import RTM_GETLINK from pyroute2.netlink.rtnl import RTM_DELLINK from pyroute2.netlink.rtnl import RTM_NEWQDISC from pyroute2.netlink.rtnl import RTM_GETQDISC from pyroute2.netlink.rtnl import RTM_DELQDISC from pyroute2.netlink.rtnl import RTM_NEWTFILTER from pyroute2.netlink.rtnl import RTM_GETTFILTER from pyroute2.netlink.rtnl import RTM_DELTFILTER from pyroute2.netlink.rtnl import RTM_NEWTCLASS from pyroute2.netlink.rtnl import RTM_GETTCLASS from pyroute2.netlink.rtnl import RTM_DELTCLASS from pyroute2.netlink.rtnl import RTM_GETNEIGH from pyroute2.netlink.rtnl import RTM_NEWRULE from pyroute2.netlink.rtnl import RTM_GETRULE from pyroute2.netlink.rtnl import RTM_DELRULE from pyroute2.netlink.rtnl import RTM_NEWROUTE from pyroute2.netlink.rtnl import RTM_GETROUTE from pyroute2.netlink.rtnl import RTM_DELROUTE from pyroute2.netlink.rtnl import RTM_SETLINK from pyroute2.netlink.rtnl import RTM_GETDHCP from pyroute2.netlink.rtnl import TC_H_INGRESS from pyroute2.netlink.rtnl import TC_H_ROOT from pyroute2.netlink.rtnl import rtprotos from pyroute2.netlink.rtnl import rtypes from pyroute2.netlink.rtnl import rtscopes from pyroute2.netlink.rtnl.req import IPLinkRequest from pyroute2.netlink.rtnl.tcmsg import get_htb_parameters from pyroute2.netlink.rtnl.tcmsg import get_htb_class_parameters from pyroute2.netlink.rtnl.tcmsg import get_tbf_parameters from pyroute2.netlink.rtnl.tcmsg import get_sfq_parameters from pyroute2.netlink.rtnl.tcmsg import get_u32_parameters from pyroute2.netlink.rtnl.tcmsg import get_netem_parameters from pyroute2.netlink.rtnl.tcmsg import get_fw_parameters from pyroute2.netlink.rtnl.tcmsg import tcmsg from pyroute2.netlink.rtnl.rtmsg import rtmsg from pyroute2.netlink.rtnl.ndmsg import ndmsg from pyroute2.netlink.rtnl.dhcpmsg import dhcpmsg from pyroute2.netlink.rtnl.ifinfmsg import ifinfmsg from pyroute2.netlink.rtnl.ifaddrmsg import ifaddrmsg from pyroute2.netlink.rtnl import IPRSocket from pyroute2.common import basestring DEFAULT_TABLE = 254 def transform_handle(handle): if isinstance(handle, basestring): (major, minor) = [int(x if x else '0', 16) for x in handle.split(':')] handle = (major << 8 * 2) | minor return handle class IPRouteMixin(object): ''' `IPRouteMixin` should not be instantiated by itself. It is intended to be used as a mixin class that provides iproute2-like API. You should use `IPRoute` or `NetNS` classes. All following info you can consider as IPRoute info as well. It is an old-school API, that provides access to rtnetlink as is. It helps you to retrieve and change almost all the data, available through rtnetlink:: from pyroute2 import IPRoute ipr = IPRoute() # lookup interface by name dev = ipr.link_lookup(ifname='tap0')[0] # bring it down ipr.link('set', dev, state='down') # change interface MAC address and rename it ipr.link('set', dev, address='00:11:22:33:44:55', ifname='vpn') # add primary IP address ipr.addr('add', dev, address='10.0.0.1', mask=24) # add secondary IP address ipr.addr('add', dev, address='10.0.0.2', mask=24) # bring it up ipr.link('set', dev, state='up') ''' # 8<--------------------------------------------------------------- # # Listing methods # def get_qdiscs(self, index=None): ''' Get all queue disciplines for all interfaces or for specified one. ''' msg = tcmsg() msg['family'] = AF_UNSPEC ret = self.nlm_request(msg, RTM_GETQDISC) if index is None: return ret else: return [x for x in ret if x['index'] == index] def get_filters(self, index=0, handle=0, parent=0): ''' Get filters for specified interface, handle and parent. ''' msg = tcmsg() msg['family'] = AF_UNSPEC msg['index'] = index msg['handle'] = handle msg['parent'] = parent return self.nlm_request(msg, RTM_GETTFILTER) def get_classes(self, index=0): ''' Get classes for specified interface. ''' msg = tcmsg() msg['family'] = AF_UNSPEC msg['index'] = index return self.nlm_request(msg, RTM_GETTCLASS) def get_links(self, *argv, **kwarg): ''' Get network interfaces. By default returns all interfaces. Arguments vector can contain interface indices or a special keyword 'all':: ip.get_links() ip.get_links('all') ip.get_links(1, 2, 3) interfaces = [1, 2, 3] ip.get_links(*interfaces) ''' result = [] links = argv or ['all'] msg_flags = NLM_F_REQUEST | NLM_F_DUMP for index in links: msg = ifinfmsg() msg['family'] = kwarg.get('family', AF_UNSPEC) if index != 'all': msg['index'] = index msg_flags = NLM_F_REQUEST result.extend(self.nlm_request(msg, RTM_GETLINK, msg_flags)) return result def get_neighbors(self, family=AF_UNSPEC): ''' Retrieve ARP cache records. ''' msg = ndmsg() msg['family'] = family return self.nlm_request(msg, RTM_GETNEIGH) def get_addr(self, family=AF_UNSPEC, index=None): ''' Get addresses:: ip.get_addr() # get all addresses ip.get_addr(index=2) # get addresses for the 2nd interface ''' msg = ifaddrmsg() msg['family'] = family ret = self.nlm_request(msg, RTM_GETADDR) if index is not None: return [x for x in ret if x.get('index') == index] else: return ret def get_dhcp(self, name, address=None): msg = dhcpmsg() msg['family'] = AF_INET msg['attrs'] = [['DHCP_IFNAME', name]] if address is not None: msg['attrs'].append(['DHCP_ADDRESS', address]) return self.nlm_request(msg, RTM_GETDHCP) def get_rules(self, family=AF_UNSPEC): ''' Get all rules. You can specify inet family, by default return rules for all families. Example:: ip.get_rules() # get all the rules for all families ip.get_routes(family=AF_INET6) # get only IPv6 rules ''' msg = ndmsg() msg['family'] = family msg_flags = NLM_F_REQUEST | NLM_F_ROOT | NLM_F_ATOMIC return self.nlm_request(msg, RTM_GETRULE, msg_flags) def get_routes(self, family=AF_UNSPEC, **kwarg): ''' Get all routes. You can specify the table. There are 255 routing classes (tables), and the kernel returns all the routes on each request. So the routine filters routes from full output. Example:: ip.get_routes() # get all the routes for all families ip.get_routes(family=AF_INET6) # get only IPv6 routes ip.get_routes(table=254) # get routes from 254 table ''' msg_flags = NLM_F_DUMP | NLM_F_REQUEST msg = rtmsg() msg['family'] = family # you can specify the table here, but the kernel # will ignore this setting table = kwarg.get('table', DEFAULT_TABLE) msg['table'] = table if table <= 255 else 252 # get a particular route if kwarg.get('dst', None) is not None: dlen = 32 if family == AF_INET else \ 128 if family == AF_INET6 else 0 msg_flags = NLM_F_REQUEST msg['dst_len'] = kwarg.get('dst_len', dlen) for key in kwarg: nla = rtmsg.name2nla(key) if kwarg[key] is not None: msg['attrs'].append([nla, kwarg[key]]) routes = self.nlm_request(msg, RTM_GETROUTE, msg_flags) return [x for x in routes if x.get_attr('RTA_TABLE') == table or kwarg.get('table', None) is None] # 8<--------------------------------------------------------------- # 8<--------------------------------------------------------------- # # Shortcuts # # addr_add(), addr_del(), route_add(), route_del() shortcuts are # removed due to redundancy. Only link shortcuts are left here for # now. Possibly, they should be moved to a separate module. # def get_default_routes(self, family=AF_UNSPEC, table=DEFAULT_TABLE): ''' Get default routes ''' # according to iproute2/ip/iproute.c:print_route() return [x for x in self.get_routes(family, table=table) if (x.get_attr('RTA_DST', None) is None and x['dst_len'] == 0)] def link_create(self, **kwarg): ''' Create a link. The method parameters will be passed to the `IPLinkRequest()` constructor as a dictionary. Examples:: ip.link_create(ifname='very_dummy', kind='dummy') ip.link_create(ifname='br0', kind='bridge') ip.link_create(ifname='v101', kind='vlan', vlan_id=101, link=1) ''' return self.link('add', **IPLinkRequest(kwarg)) def link_up(self, index): ''' Switch an interface up unconditionally. ''' self.link('set', index=index, state='up') def link_down(self, index): ''' Switch an interface down unconditilnally. ''' self.link('set', index=index, state='down') def link_rename(self, index, name): ''' Rename an interface. Please note, that the interface must be in the `DOWN` state in order to be renamed, otherwise you will get an error. ''' self.link('set', index=index, ifname=name) def link_remove(self, index): ''' Remove an interface ''' self.link('delete', index=index) def link_lookup(self, **kwarg): ''' Lookup interface index (indeces) by first level NLA value. Example:: ip.link_lookup(address="52:54:00:9d:4e:3d") ip.link_lookup(ifname="lo") ip.link_lookup(operstate="UP") Please note, that link_lookup() returns list, not one value. ''' name = tuple(kwarg.keys())[0] value = kwarg[name] name = str(name).upper() if not name.startswith('IFLA_'): name = 'IFLA_%s' % (name) return [k['index'] for k in [i for i in self.get_links() if 'attrs' in i] if [l for l in k['attrs'] if l[0] == name and l[1] == value]] def flush_routes(self, *argv, **kwarg): ''' Flush routes -- purge route records from a table. Arguments are the same as for `get_routes()` routine. Actually, this routine implements a pipe from `get_routes()` to `nlm_request()`. ''' flags = NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL | NLM_F_REQUEST ret = [] kwarg['table'] = kwarg.get('table', DEFAULT_TABLE) for route in self.get_routes(*argv, **kwarg): ret.append(self.nlm_request(route, msg_type=RTM_DELROUTE, msg_flags=flags)) return ret # 8<--------------------------------------------------------------- # 8<--------------------------------------------------------------- # # General low-level configuration methods # def link(self, command, **kwarg): ''' Link operations. * command -- set, add or delete * index -- device index * \*\*kwarg -- keywords, NLA Example:: x = 62 # interface index ip.link("set", index=x, state="down") ip.link("set", index=x, address="00:11:22:33:44:55", name="bala") ip.link("set", index=x, mtu=1000, txqlen=2000) ip.link("set", index=x, state="up") Keywords "state", "flags" and "mask" are reserved. State can be "up" or "down", it is a shortcut:: state="up": flags=1, mask=1 state="down": flags=0, mask=0 For more flags grep IFF in the kernel code, until we write human-readable flag resolver. Other keywords are from ifinfmsg.nla_map, look into the corresponding module. You can use the form "ifname" as well as "IFLA_IFNAME" and so on, so that's equal:: ip.link("set", index=x, mtu=1000) ip.link("set", index=x, IFLA_MTU=1000) You can also delete interface with:: ip.link("delete", index=x) ''' commands = {'set': RTM_SETLINK, 'add': RTM_NEWLINK, 'del': RTM_DELLINK, 'remove': RTM_DELLINK, 'delete': RTM_DELLINK} command = commands.get(command, command) msg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL msg = ifinfmsg() # index is required msg['index'] = kwarg.get('index') flags = kwarg.pop('flags', 0) or 0 mask = kwarg.pop('mask', 0) or kwarg.pop('change', 0) or 0 if 'state' in kwarg: mask = 1 # IFF_UP mask if kwarg['state'].lower() == 'up': flags = 1 # 0 (down) or 1 (up) del kwarg['state'] msg['flags'] = flags msg['change'] = mask for key in kwarg: nla = type(msg).name2nla(key) if kwarg[key] is not None: msg['attrs'].append([nla, kwarg[key]]) return self.nlm_request(msg, msg_type=command, msg_flags=msg_flags) def addr(self, command, index, address, mask=24, family=None, scope=0, **kwarg): ''' Address operations * command -- add, delete * index -- device index * address -- IPv4 or IPv6 address * mask -- address mask * family -- socket.AF_INET for IPv4 or socket.AF_INET6 for IPv6 * scope -- the address scope, see /etc/iproute2/rt_scopes Example:: index = 62 ip.addr("add", index, address="10.0.0.1", mask=24) ip.addr("add", index, address="10.0.0.2", mask=24) ''' commands = {'add': RTM_NEWADDR, 'del': RTM_DELADDR, 'remove': RTM_DELADDR, 'delete': RTM_DELADDR} command = commands.get(command, command) flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL # try to guess family, if it is not forced if family is None: if address.find(":") > -1: family = AF_INET6 else: family = AF_INET msg = ifaddrmsg() msg['index'] = index msg['family'] = family msg['prefixlen'] = mask msg['scope'] = scope if family == AF_INET: msg['attrs'] = [['IFA_LOCAL', address], ['IFA_ADDRESS', address]] elif family == AF_INET6: msg['attrs'] = [['IFA_ADDRESS', address]] for key in kwarg: nla = ifaddrmsg.name2nla(key) if kwarg[key] is not None: msg['attrs'].append([nla, kwarg[key]]) return self.nlm_request(msg, msg_type=command, msg_flags=flags, terminate=lambda x: x['header']['type'] == NLMSG_ERROR) def tc(self, command, kind, index, handle=0, **kwarg): ''' "Swiss knife" for traffic control. With the method you can add, delete or modify qdiscs, classes and filters. * command -- add or delete qdisc, class, filter. * kind -- a string identifier -- "sfq", "htb", "u32" and so on. * handle -- integer or string Command can be one of ("add", "del", "add-class", "del-class", "add-filter", "del-filter") (see `commands` dict in the code). Handle notice: traditional iproute2 notation, like "1:0", actually represents two parts in one four-bytes integer:: 1:0 -> 0x10000 1:1 -> 0x10001 ff:0 -> 0xff0000 ffff:1 -> 0xffff0001 For pyroute2 tc() you can use both forms: integer like 0xffff0000 or string like 'ffff:0000'. By default, handle is 0, so you can add simple classless queues w/o need to specify handle. Ingress queue causes handle to be 0xffff0000. So, to set up sfq queue on interface 1, the function call will be like that:: ip = IPRoute() ip.tc("add", "sfq", 1) Instead of string commands ("add", "del"...), you can use also module constants, `RTM_NEWQDISC`, `RTM_DELQDISC` and so on:: ip = IPRoute() ip.tc(RTM_NEWQDISC, "sfq", 1) More complex example with htb qdisc, lets assume eth0 == 2:: # u32 --> +--> htb 1:10 --> sfq 10:0 # | | # | | # eth0 -- htb 1:0 -- htb 1:1 # | | # | | # u32 --> +--> htb 1:20 --> sfq 20:0 eth0 = 2 # add root queue 1:0 ip.tc("add", "htb", eth0, 0x10000, default=0x200000) # root class 1:1 ip.tc("add-class", "htb", eth0, 0x10001, parent=0x10000, rate="256kbit", burst=1024 * 6) # two branches: 1:10 and 1:20 ip.tc("add-class", "htb", eth0, 0x10010, parent=0x10001, rate="192kbit", burst=1024 * 6, prio=1) ip.tc("add-class", "htb", eht0, 0x10020, parent=0x10001, rate="128kbit", burst=1024 * 6, prio=2) # two leaves: 10:0 and 20:0 ip.tc("add", "sfq", eth0, 0x100000, parent=0x10010, perturb=10) ip.tc("add", "sfq", eth0, 0x200000, parent=0x10020, perturb=10) # two filters: one to load packets into 1:10 and the # second to 1:20 ip.tc("add-filter", "u32", eth0, parent=0x10000, prio=10, protocol=socket.AF_INET, target=0x10010, keys=["0x0006/0x00ff+8", "0x0000/0xffc0+2"]) ip.tc("add-filter", "u32", eth0, parent=0x10000, prio=10, protocol=socket.AF_INET, target=0x10020, keys=["0x5/0xf+0", "0x10/0xff+33"]) ''' commands = {'add': RTM_NEWQDISC, 'del': RTM_DELQDISC, 'remove': RTM_DELQDISC, 'delete': RTM_DELQDISC, 'add-class': RTM_NEWTCLASS, 'del-class': RTM_DELTCLASS, 'add-filter': RTM_NEWTFILTER, 'del-filter': RTM_DELTFILTER} command = commands.get(command, command) flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL msg = tcmsg() # transform handle, parent and target, if needed: handle = transform_handle(handle) for item in ('parent', 'target', 'default'): if item in kwarg and kwarg[item] is not None: kwarg[item] = transform_handle(kwarg[item]) msg['index'] = index msg['handle'] = handle opts = kwarg.get('opts', None) if kind == 'ingress': msg['parent'] = TC_H_INGRESS msg['handle'] = 0xffff0000 elif kind == 'tbf': msg['parent'] = TC_H_ROOT if kwarg: opts = get_tbf_parameters(kwarg) elif kind == 'htb': msg['parent'] = kwarg.get('parent', TC_H_ROOT) if kwarg: if command in (RTM_NEWQDISC, RTM_DELQDISC): opts = get_htb_parameters(kwarg) elif command in (RTM_NEWTCLASS, RTM_DELTCLASS): opts = get_htb_class_parameters(kwarg) elif kind == 'netem': msg['parent'] = kwarg.get('parent', TC_H_ROOT) if kwarg: opts = get_netem_parameters(kwarg) elif kind == 'sfq': msg['parent'] = kwarg.get('parent', TC_H_ROOT) if kwarg: opts = get_sfq_parameters(kwarg) elif kind == 'u32': msg['parent'] = kwarg.get('parent') msg['info'] = htons(kwarg.get('protocol', 0) & 0xffff) |\ ((kwarg.get('prio', 0) << 16) & 0xffff0000) if kwarg: opts = get_u32_parameters(kwarg) elif kind == 'fw': msg['parent'] = kwarg.get('parent') msg['info'] = htons(kwarg.get('protocol', 0) & 0xffff) |\ ((kwarg.get('prio', 0) << 16) & 0xffff0000) if kwarg: opts = get_fw_parameters(kwarg) else: msg['parent'] = kwarg.get('parent', TC_H_ROOT) if kind is not None: msg['attrs'] = [['TCA_KIND', kind]] if opts is not None: msg['attrs'].append(['TCA_OPTIONS', opts]) return self.nlm_request(msg, msg_type=command, msg_flags=flags) def route(self, command, rtype='RTN_UNICAST', rtproto='RTPROT_STATIC', rtscope='RT_SCOPE_UNIVERSE', **kwarg): ''' Route operations * command -- add, delete * prefix -- route prefix * mask -- route prefix mask * rtype -- route type (default: "RTN_UNICAST") * rtproto -- routing protocol (default: "RTPROT_STATIC") * rtscope -- routing scope (default: "RT_SCOPE_UNIVERSE") * family -- socket.AF_INET (default) or socket.AF_INET6 `pyroute2/netlink/rtnl/rtmsg.py` rtmsg.nla_map: * table -- routing table to use (default: 254) * gateway -- via address * prefsrc -- preferred source IP address * dst -- the same as `prefix` * src -- source address * iif -- incoming traffic interface * oif -- outgoing traffic interface etc. Example:: ip.route("add", dst="10.0.0.0", mask=24, gateway="192.168.0.1") ''' # 8<---------------------------------------------------- # FIXME # flags should be moved to some more general place flags_base = NLM_F_REQUEST | NLM_F_ACK flags_make = flags_base | NLM_F_CREATE | NLM_F_EXCL flags_replace = flags_base | NLM_F_REPLACE # 8<---------------------------------------------------- commands = {'add': (RTM_NEWROUTE, flags_make), 'set': (RTM_NEWROUTE, flags_replace), 'del': (RTM_DELROUTE, flags_make), 'remove': (RTM_DELROUTE, flags_make), 'delete': (RTM_DELROUTE, flags_make)} (command, flags) = commands.get(command, command) msg = rtmsg() # table is mandatory; by default == 254 # if table is not defined in kwarg, save it there # also for nla_attr: table = kwarg.get('table', 254) msg['table'] = table if table <= 255 else 252 msg['family'] = kwarg.get('family', AF_INET) msg['proto'] = rtprotos[rtproto] msg['type'] = rtypes[rtype] msg['scope'] = rtscopes[rtscope] msg['dst_len'] = kwarg.get('dst_len', None) or \ kwarg.get('mask', 0) msg['attrs'] = [] # FIXME # deprecated "prefix" support: if 'prefix' in kwarg: kwarg['dst'] = kwarg['prefix'] for key in kwarg: nla = rtmsg.name2nla(key) if kwarg[key] is not None: msg['attrs'].append([nla, kwarg[key]]) return self.nlm_request(msg, msg_type=command, msg_flags=flags) def rule(self, command, table, priority=32000, rtype='RTN_UNICAST', rtscope='RT_SCOPE_UNIVERSE', family=AF_INET, src=None, src_len=None, dst=None, dst_len=None, fwmark=None): ''' Rule operations * command - add, delete * table - 0 < table id < 253 * priority - 0 < rule's priority < 32766 * rtype - type of rule, default 'RTN_UNICAST' * rtscope - routing scope, default RT_SCOPE_UNIVERSE (RT_SCOPE_UNIVERSE|RT_SCOPE_SITE|\ RT_SCOPE_LINK|RT_SCOPE_HOST|RT_SCOPE_NOWHERE) * family - rule's family (socket.AF_INET (default) or socket.AF_INET6) * src - IP source for Source Based (Policy Based) routing's rule * dst - IP for Destination Based (Policy Based) routing's rule * src_len - Mask for Source Based (Policy Based) routing's rule * dst_len - Mask for Destination Based (Policy Based) routing's rule Example:: ip.rule('add', 10, 32000) Will create:: #ip ru sh ... 32000: from all lookup 10 .... Example:: iproute.rule('add', 11, 32001, 'RTN_UNREACHABLE') Will create:: #ip ru sh ... 32001: from all lookup 11 unreachable .... Example:: iproute.rule('add', 14, 32004, src='10.64.75.141') Will create:: #ip ru sh ... 32004: from 10.64.75.141 lookup 14 ... Example:: iproute.rule('add', 15, 32005, dst='10.64.75.141', dst_len=24) Will create:: #ip ru sh ... 32005: from 10.64.75.141/24 lookup 15 ... Example:: iproute.rule('add', 15, 32006, dst='10.64.75.141', fwmark=10) Will create:: #ip ru sh ... 32006: from 10.64.75.141 fwmark 0xa lookup 15 ... ''' if table < 1: raise ValueError('unsupported table number') commands = {'add': RTM_NEWRULE, 'del': RTM_DELRULE, 'remove': RTM_DELRULE, 'delete': RTM_DELRULE} command = commands.get(command, command) msg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL msg = rtmsg() msg['table'] = table if table <= 255 else 252 msg['family'] = family msg['type'] = rtypes[rtype] msg['scope'] = rtscopes[rtscope] msg['attrs'] = [['RTA_TABLE', table]] msg['attrs'].append(['RTA_PRIORITY', priority]) if fwmark is not None: msg['attrs'].append(['RTA_PROTOINFO', fwmark]) addr_len = {AF_INET6: 128, AF_INET: 32}[family] if(dst_len is not None and dst_len >= 0 and dst_len <= addr_len): msg['dst_len'] = dst_len else: msg['dst_len'] = 0 if(src_len is not None and src_len >= 0 and src_len <= addr_len): msg['src_len'] = src_len else: msg['src_len'] = 0 if src is not None: msg['attrs'].append(['RTA_SRC', src]) if src_len is None: msg['src_len'] = addr_len if dst is not None: msg['attrs'].append(['RTA_DST', dst]) if dst_len is None: msg['dst_len'] = addr_len return self.nlm_request(msg, msg_type=command, msg_flags=msg_flags) # 8<--------------------------------------------------------------- class IPRoute(IPRouteMixin, IPRSocket): ''' Production class that provides iproute API over normal Netlink socket. You can think of this class in some way as of plain old iproute2 utility. ''' pass pyroute2-0.3.5/pyroute2/netlink/0000775000175000017500000000000012474420674016475 5ustar peetpeet00000000000000pyroute2-0.3.5/pyroute2/netlink/ipq/0000775000175000017500000000000012474420674017266 5ustar peetpeet00000000000000pyroute2-0.3.5/pyroute2/netlink/ipq/__init__.py0000664000175000017500000000661012441630373021373 0ustar peetpeet00000000000000''' IPQ -- userspace firewall ========================= Netlink family for dealing with `QUEUE` iptables target. All the packets routed to the target `QUEUE` should be handled by a userspace program and the program should response with a verdict. E.g., the verdict can be `NF_DROP` and in that case the packet will be silently dropped, or `NF_ACCEPT`, and the packet will be pass the rule. ''' from pyroute2.netlink import NLM_F_REQUEST from pyroute2.netlink import nlmsg from pyroute2.netlink.nlsocket import NetlinkSocket from pyroute2.netlink.nlsocket import Marshal # constants IFNAMSIZ = 16 IPQ_MAX_PAYLOAD = 0x800 # IPQ messages IPQM_BASE = 0x10 IPQM_MODE = IPQM_BASE + 1 IPQM_VERDICT = IPQM_BASE + 2 IPQM_PACKET = IPQM_BASE + 3 # IPQ modes IPQ_COPY_NONE = 0 IPQ_COPY_META = 1 IPQ_COPY_PACKET = 2 # verdict types NF_DROP = 0 NF_ACCEPT = 1 NF_STOLEN = 2 NF_QUEUE = 3 NF_REPEAT = 4 NF_STOP = 5 class ipq_base_msg(nlmsg): def decode(self): nlmsg.decode(self) self['payload'] = self.buf.read(self['data_len']) def encode(self): init = self.buf.tell() nlmsg.encode(self) if 'payload' in self: self.buf.write(self['payload']) self.update_length(init) class ipq_packet_msg(ipq_base_msg): fields = (('packet_id', 'L'), ('mark', 'L'), ('timestamp_sec', 'l'), ('timestamp_usec', 'l'), ('hook', 'I'), ('indev_name', '%is' % IFNAMSIZ), ('outdev_name', '%is' % IFNAMSIZ), ('hw_protocol', '>H'), ('hw_type', 'H'), ('hw_addrlen', 'B'), ('hw_addr', '6B'), ('__pad', '9x'), ('data_len', 'I'), ('__pad', '4x')) class ipq_mode_msg(nlmsg): pack = 'struct' fields = (('value', 'B'), ('__pad', '7x'), ('range', 'I'), ('__pad', '12x')) class ipq_verdict_msg(ipq_base_msg): pack = 'struct' fields = (('value', 'I'), ('__pad', '4x'), ('id', 'L'), ('data_len', 'I'), ('__pad', '4x')) class MarshalIPQ(Marshal): msg_map = {IPQM_MODE: ipq_mode_msg, IPQM_VERDICT: ipq_verdict_msg, IPQM_PACKET: ipq_packet_msg} class IPQSocket(NetlinkSocket): ''' Low-level socket interface. Provides all the usual socket does, can be used in poll/select, doesn't create any implicit threads. ''' def bind(self, mode=IPQ_COPY_PACKET): ''' Bind the socket and performs IPQ mode configuration. The only parameter is mode, the default value is IPQ_COPY_PACKET (copy all the packet data). ''' NetlinkSocket.bind(self, groups=0, pid=0) self.register_policy(MarshalIPQ.msg_map) msg = ipq_mode_msg() msg['value'] = mode msg['range'] = IPQ_MAX_PAYLOAD msg['header']['type'] = IPQM_MODE msg['header']['flags'] = NLM_F_REQUEST msg.encode() self.sendto(msg.buf.getvalue(), (0, 0)) def verdict(self, seq, v): ''' Issue a verdict `v` for a packet `seq`. ''' msg = ipq_verdict_msg() msg['value'] = v msg['id'] = seq msg['data_len'] = 0 msg['header']['type'] = IPQM_VERDICT msg['header']['flags'] = NLM_F_REQUEST msg.encode() self.sendto(msg.buf.getvalue(), (0, 0)) pyroute2-0.3.5/pyroute2/netlink/__init__.py0000664000175000017500000011044612472731760020613 0ustar peetpeet00000000000000''' Netlink basics ============== General netlink packet structure:: nlmsg packet: + header + data Generic netlink message header:: nlmsg header: + uint32 length + uint16 type + uint16 flags + uint32 sequence number + uint32 pid The `length` field is the length of all the packet, including data and header. The `type` field is used to distinguish different message types, commands etc. Please note, that there is no explicit protocol field -- you choose a netlink protocol, when you create a socket. The `sequence number` is very important. Netlink is an asynchronous protocol -- it means, that the packet order doesn't matter and is not guaranteed. But responses to a request are always marked with the same sequence number, so you can treat it as a cookie. Please keep in mind, that a netlink request can initiate a cascade of events, and netlink messages from these events can carry sequence number == 0. E.g., it is so when you remove a primary IP addr from an interface, when `promote_secondaries` sysctl is set. Beside of incapsulated headers and other protocol-specific data, netlink messages can carry NLA (netlink attributes). NLA structure is as follows:: NLA header: + uint16 length + uint16 type NLA data: + data-specific struct # optional: + NLA + NLA + ... So, NLA structures can be nested, forming a tree. Complete structure of a netlink packet:: nlmsg header: + uint32 length + uint16 type + uint16 flags + uint32 sequence number + uint32 pid [ optional protocol-specific data ] [ optional NLA tree ] More information about netlink protocol you can find in the man pages. Pyroute2 and netlink ==================== packets +++++++ To simplify the development, pyroute2 provides an easy way to describe packet structure. As an example, you can take the ifaddrmsg description -- `pyroute2/netlink/rtnl/ifaddrmsg.py`. To describe a packet, you need to inherit from `nlmsg` class:: from pyroute2.netlink import nlmsg class foo_msg(nlmsg): fields = ( ... ) nla_map = ( ... ) NLA are described in the same way, but the parent class should be `nla`, instead of `nlmsg`. And yes, it is important to use the proper parent class -- it affects the header structure. fields attribute ++++++++++++++++ The `fields` attribute describes the structure of the protocol-specific data. It is a tuple of tuples, where each member contains a field name and its data format. Field data format should be specified as for Python `struct` module. E.g., ifaddrmsg structure:: ifaddrmsg structure: + unsigned char ifa_family + unsigned char ifa_prefixlen + unsigned char ifa_flags + unsigned char ifa_scope + int ifa_index should be described as follows:: class ifaddrmsg(nlmsg): fields = (('family', 'B'), ('prefixlen', 'B'), ('flags', 'B'), ('scope', 'B'), ('index', 'I')) Format strings are passed directly to the `struct` module, so you can use all the notations like `>I`, `16s` etc. All fields are parsed from the stream separately, so if you want to explicitly fix alignemt, as if it were C struct, use the `pack` attribute:: class tstats(nla): pack = 'struct' fields = (('version', 'H'), ('ac_exitcode', 'I'), ('ac_flag', 'B'), ...) Explicit padding bytes also can be used, when struct packing doesn't work well:: class ipq_mode_msg(nlmsg): pack = 'struct' fields = (('value', 'B'), ('__pad', '7x'), ('range', 'I'), ('__pad', '12x')) nla_map attribute +++++++++++++++++ The `nla_map` attribute is a tuple of NLA descriptions. Each description is also a tuple in two different forms: either two fields, name and format, or three fields: type, name and format. Please notice, that the format field is a string name of corresponding NLA class:: class ifaddrmsg(nlmsg): ... nla_map = (('IFA_UNSPEC', 'hex'), ('IFA_ADDRESS', 'ipaddr'), ('IFA_LOCAL', 'ipaddr'), ...) This code will create mapping, where IFA_ADDRESS NLA will be of type 1 and IFA_LOCAL -- of type 2, etc. Both NLA will be decoded as IP addresses (class `ipaddr`). IFA_UNSPEC will be of type 0, and if it will be in the NLA tree, it will be just dumped in hex. NLA class names are should be specified as strings, since they are resolved in runtime. There are several pre-defined NLA types, that you will get with `nla` class: * none # forces pyroute2 just to skip this NLA * uint8 * uint16 * uint32 # there are dedicated NLA of these types as well * ipaddr # IP address, IPv4 or IPv6, depending on the socket * l2addr # MAC address * hex # hex dump as a string -- useful for debugging * cdata # just a binary string * asciiz # zero-terminated ASCII string Please refer to `pyroute2/netlink/__init__.py` for details. You can also make your own NLA descriptions:: class ifaddrmsg(nlmsg): ... nla_map = (... ('IFA_CACHEINFO', 'cacheinfo'), ...) class cacheinfo(nla): fields = (('ifa_prefered', 'I'), ('ifa_valid', 'I'), ('cstamp', 'I'), ('tstamp', 'I')) Custom NLA descriptions should be defined in the same class, where they are used. Also, it is possible to use not autogenerated type numbers, as for ifaddrmsg, but specify them explicitly:: class iw_event(nla): ... nla_map = ((0x8B00, 'SIOCSIWCOMMIT', 'hex'), (0x8B01, 'SIOCGIWNAME', 'hex'), (0x8B02, 'SIOCSIWNWID', 'hex'), (0x8B03, 'SIOCGIWNWID', 'hex'), ...) Here you can see custom NLA type numbers -- 0x8B00, 0x8B01 etc. It is not permitted to mix these two forms in one class: you should use ether autogenerated type numbers (two fields tuples), or explicit numbers (three fields typles). parsed netlink message ++++++++++++++++++++++ Netlink messages are represented by pyroute2 as dictionaries as follows:: {'header': {'pid': ..., 'length: ..., 'flags': ..., 'error': None, # if you are lucky 'type': ..., 'sequence_number': ...}, # fields attributes 'field_name1': value, ... 'field_nameX': value, # nla tree 'attrs': [['NLA_NAME1', value], ... ['NLA_NAMEX', value], ['NLA_NAMEY', {'field_name1': value, ... 'field_nameX': value, 'attrs': [['NLA_NAME.... ]]}]]} As an example, a message from the wireless subsystem about new scan event:: {'index': 4, 'family': 0, '__align': 0, 'header': {'pid': 0, 'length': 64, 'flags': 0, 'error': None, 'type': 16, 'sequence_number': 0}, 'flags': 69699, 'ifi_type': 1, 'event': 'RTM_NEWLINK', 'change': 0, 'attrs': [['IFLA_IFNAME', 'wlp3s0'], ['IFLA_WIRELESS', {'attrs': [['SIOCGIWSCAN', '00:00:00:00:00:00:00:00:00:00:00:00']]}]]} create and send messages ++++++++++++++++++++++++ Using high-level interfaces like `IPRoute` or `IPDB`, you will never need to manually construct and send netlink messages. But in the case you really need it, it is simple as well. Having a description class, like `ifaddrmsg` from above, you need to: * instantiate it * fill the fields * encode the packet * send the encoded data The code:: from pyroute2.netlink import NLM_F_REQUEST from pyroute2.netlink import NLM_F_ACK from pyroute2.netlink import NLM_F_CREATE from pyroute2.netlink import NLM_F_EXCL from pyroute2.iproute import RTM_NEWADDR from pyroute2.netlink.rtnl.ifaddrmsg import ifaddrmsg ## # add an addr to an interface # # create the message msg = ifaddrmsg() # fill the protocol-specific fields msg['index'] = index # index of the interface msg['family'] = AF_INET # address family msg['prefixlen'] = 24 # the address mask msg['scope'] = scope # see /etc/iproute2/rt_scopes # attach NLA -- it MUST be a list / mutable msg['attrs'] = [['IFA_LOCAL', '192.168.0.1'], ['IFA_ADDRESS', '192.162.0.1']] # fill generic netlink fields msg['header']['sequence_number'] = nonce # an unique seq number msg['header']['pid'] = os.getpid() msg['header']['type'] = RTM_NEWADDR msg['header']['flags'] = NLM_F_REQUEST |\\ NLM_F_ACK |\\ NLM_F_CREATE |\\ NLM_F_EXCL # encode the packet msg.encode() # send the buffer nlsock.sendto(msg.buf.getvalue(), (0, 0)) Please notice, that NLA list *MUST* be mutable. ------------------ Module contents: ''' import traceback import logging import socket import struct import types import sys import io import re import os from pyroute2.common import hexdump from pyroute2.common import basestring _letters = re.compile('[A-Za-z]') _fmt_letters = re.compile('[^!><@=][!><@=]') ## # That's a hack for the code linter, which works under # Python3, see unicode reference in the code below if sys.version[0] == '3': unicode = str NLMSG_MIN_TYPE = 0x10 GENL_NAMSIZ = 16 # length of family name GENL_MIN_ID = NLMSG_MIN_TYPE GENL_MAX_ID = 1023 GENL_ADMIN_PERM = 0x01 GENL_CMD_CAP_DO = 0x02 GENL_CMD_CAP_DUMP = 0x04 GENL_CMD_CAP_HASPOL = 0x08 # # List of reserved static generic netlink identifiers: # GENL_ID_GENERATE = 0 GENL_ID_CTRL = NLMSG_MIN_TYPE # # Controller # CTRL_CMD_UNSPEC = 0x0 CTRL_CMD_NEWFAMILY = 0x1 CTRL_CMD_DELFAMILY = 0x2 CTRL_CMD_GETFAMILY = 0x3 CTRL_CMD_NEWOPS = 0x4 CTRL_CMD_DELOPS = 0x5 CTRL_CMD_GETOPS = 0x6 CTRL_CMD_NEWMCAST_GRP = 0x7 CTRL_CMD_DELMCAST_GRP = 0x8 CTRL_CMD_GETMCAST_GRP = 0x9 # unused CTRL_ATTR_UNSPEC = 0x0 CTRL_ATTR_FAMILY_ID = 0x1 CTRL_ATTR_FAMILY_NAME = 0x2 CTRL_ATTR_VERSION = 0x3 CTRL_ATTR_HDRSIZE = 0x4 CTRL_ATTR_MAXATTR = 0x5 CTRL_ATTR_OPS = 0x6 CTRL_ATTR_MCAST_GROUPS = 0x7 CTRL_ATTR_OP_UNSPEC = 0x0 CTRL_ATTR_OP_ID = 0x1 CTRL_ATTR_OP_FLAGS = 0x2 CTRL_ATTR_MCAST_GRP_UNSPEC = 0x0 CTRL_ATTR_MCAST_GRP_NAME = 0x1 CTRL_ATTR_MCAST_GRP_ID = 0x2 # Different Netlink families # NETLINK_ROUTE = 0 # Routing/device hook NETLINK_UNUSED = 1 # Unused number NETLINK_USERSOCK = 2 # Reserved for user mode socket protocols NETLINK_FIREWALL = 3 # Firewalling hook NETLINK_INET_DIAG = 4 # INET socket monitoring NETLINK_NFLOG = 5 # netfilter/iptables ULOG NETLINK_XFRM = 6 # ipsec NETLINK_SELINUX = 7 # SELinux event notifications NETLINK_ISCSI = 8 # Open-iSCSI NETLINK_AUDIT = 9 # auditing NETLINK_FIB_LOOKUP = 10 NETLINK_CONNECTOR = 11 NETLINK_NETFILTER = 12 # netfilter subsystem NETLINK_IP6_FW = 13 NETLINK_DNRTMSG = 14 # DECnet routing messages NETLINK_KOBJECT_UEVENT = 15 # Kernel messages to userspace NETLINK_GENERIC = 16 # leave room for NETLINK_DM (DM Events) NETLINK_SCSITRANSPORT = 18 # SCSI Transports NLMSG_ALIGNTO = 4 class NetlinkError(Exception): ''' Base netlink error ''' def __init__(self, code, msg=None): msg = msg or os.strerror(code) super(NetlinkError, self).__init__(code, msg) self.code = code class NetlinkDecodeError(Exception): ''' Base decoding error class. Incapsulates underlying error for the following analysis ''' def __init__(self, exception): self.exception = exception class NetlinkHeaderDecodeError(NetlinkDecodeError): ''' The error occured while decoding a header ''' pass class NetlinkDataDecodeError(NetlinkDecodeError): ''' The error occured while decoding the message fields ''' pass class NetlinkNLADecodeError(NetlinkDecodeError): ''' The error occured while decoding NLA chain ''' pass def NLMSG_ALIGN(l): return (l + NLMSG_ALIGNTO - 1) & ~ (NLMSG_ALIGNTO - 1) class NotInitialized(Exception): pass # Netlink message flags values (nlmsghdr.flags) # NLM_F_REQUEST = 1 # It is request message. NLM_F_MULTI = 2 # Multipart message, terminated by NLMSG_DONE NLM_F_ACK = 4 # Reply with ack, with zero or error code NLM_F_ECHO = 8 # Echo this request # Modifiers to GET request NLM_F_ROOT = 0x100 # specify tree root NLM_F_MATCH = 0x200 # return all matching NLM_F_ATOMIC = 0x400 # atomic GET NLM_F_DUMP = (NLM_F_ROOT | NLM_F_MATCH) # Modifiers to NEW request NLM_F_REPLACE = 0x100 # Override existing NLM_F_EXCL = 0x200 # Do not touch, if it exists NLM_F_CREATE = 0x400 # Create, if it does not exist NLM_F_APPEND = 0x800 # Add to end of list NLMSG_NOOP = 0x1 # Nothing NLMSG_ERROR = 0x2 # Error NLMSG_DONE = 0x3 # End of a dump NLMSG_OVERRUN = 0x4 # Data lost NLMSG_CONTROL = 0xe # Custom message type for messaging control NLMSG_TRANSPORT = 0xf # Custom message type for NL as a transport NLMSG_MIN_TYPE = 0x10 # < 0x10: reserved control messages NLMSG_MAX_LEN = 0xffff # Max message length mtypes = {1: 'NLMSG_NOOP', 2: 'NLMSG_ERROR', 3: 'NLMSG_DONE', 4: 'NLMSG_OVERRUN'} IPRCMD_NOOP = 0 IPRCMD_STOP = 1 IPRCMD_ACK = 2 IPRCMD_ERR = 3 IPRCMD_REGISTER = 4 IPRCMD_RELOAD = 5 IPRCMD_ROUTE = 6 IPRCMD_CONNECT = 7 IPRCMD_DISCONNECT = 8 IPRCMD_SERVE = 9 IPRCMD_SHUTDOWN = 10 IPRCMD_SUBSCRIBE = 11 IPRCMD_UNSUBSCRIBE = 12 IPRCMD_PROVIDE = 13 IPRCMD_REMOVE = 14 IPRCMD_DISCOVER = 15 IPRCMD_UNREGISTER = 16 class nlmsg_base(dict): ''' Netlink base class. You do not need to inherit it directly, unless you're inventing completely new protocol structure. Use nlmsg or nla classes. The class provides several methods, but often one need to customize only `decode()` and `encode()`. ''' fields = [] # data field names, to build a dictionary header = None # optional header class pack = None # pack pragma nla_map = {} # NLA mapping def __init__(self, buf=None, length=None, parent=None, debug=False): dict.__init__(self) for i in self.fields: self[i[0]] = 0 # FIXME: only for number values self.raw = None self.debug = debug self.length = length or 0 self.parent = parent self.offset = 0 self.prefix = None self['attrs'] = [] self['value'] = NotInitialized self.value = NotInitialized self.register_nlas() self.reset(buf) if self.header is not None: self['header'] = self.header(self.buf) def copy(self): ''' Return a decoded copy of the netlink message. Works correctly only if the message was encoded, or is received from the socket. ''' ret = type(self)(self.buf.getvalue()) ret.decode() return ret def reset(self, buf=None): ''' Reset the message buffer. Optionally, set the message from the `buf` parameter. This parameter can be either string, or io.BytesIO, or dict instance. ''' if isinstance(buf, basestring): b = io.BytesIO() b.write(buf) b.seek(0) buf = b if isinstance(buf, dict): self.setvalue(buf) buf = None self.buf = buf or io.BytesIO() if 'header' in self: self['header'].buf = self.buf def _strip_one(self, name): for i in tuple(self['attrs']): if i[0] == name: self['attrs'].remove(i) return self def strip(self, attrs): ''' Remove an NLA from the attrs chain. The `attrs` parameter can be either string, or iterable. In the latter case, will be stripped NLAs, specified in the provided list. ''' if isinstance(attrs, basestring): self._strip_one(attrs) else: for name in attrs: self._strip_one(name) return self def __ops(self, rvalue, op0, op1): lvalue = self.getvalue() res = self.__class__() for key in lvalue: if key not in ('header', 'attrs'): if op0 == '__sub__': # operator -, complement if (key not in rvalue) or (lvalue[key] != rvalue[key]): res[key] = lvalue[key] elif op0 == '__and__': # operator &, intersection if (key in rvalue) and (lvalue[key] == rvalue[key]): res[key] = lvalue[key] if 'attrs' in lvalue: res['attrs'] = [] for attr in lvalue['attrs']: if isinstance(attr[1], nla): diff = getattr(attr[1], op0)(rvalue.get_attr(attr[0])) if diff is not None: res['attrs'].append([attr[0], diff]) else: if op0 == '__sub__': # operator -, complement if rvalue.get_attr(attr[0]) != attr[1]: res['attrs'].append(attr) elif op0 == '__and__': # operator &, intersection if rvalue.get_attr(attr[0]) == attr[1]: res['attrs'].append(attr) if not len(res): return None else: if 'header' in res: del res['header'] if 'value' in res: del res['value'] if 'attrs' in res and not len(res['attrs']): del res['attrs'] return res def __sub__(self, rvalue): ''' Subjunction operation. ''' return self.__ops(rvalue, '__sub__', '__ne__') def __and__(self, rvalue): ''' Conjunction operation. ''' return self.__ops(rvalue, '__and__', '__eq__') def __eq__(self, rvalue): ''' Having nla, we are able to use it in operations like:: if nla == 'some value': ... ''' lvalue = self.getvalue() if lvalue is self: for key in self: try: assert self.get(key) == rvalue.get(key) except Exception: # on any error -- is not equal return False return True else: return lvalue == rvalue @classmethod def get_size(self): size = 0 for field in self.fields: size += struct.calcsize(field[1]) return size @classmethod def nla2name(self, name): ''' Convert NLA name into human-friendly name Example: IFLA_ADDRESS -> address Requires self.prefix to be set ''' return name[(name.find(self.prefix) + 1) * len(self.prefix):].lower() @classmethod def name2nla(self, name): ''' Convert human-friendly name into NLA name Example: address -> IFLA_ADDRESS Requires self.prefix to be set ''' name = name.upper() if name.find(self.prefix) == -1: name = "%s%s" % (self.prefix, name) return name def reserve(self): ''' Reserve space in the buffer for data. This can be used to skip encoding of the header until some fields will be known. ''' size = 0 for i in self.fields: size += struct.calcsize(i[1]) self.buf.seek(size, 1) def decode(self): ''' Decode the message. The message should have the `buf` attribute initialized. e.g.:: data = sock.recv(16384) msg = ifinfmsg(data) If you want to customize the decoding process, override the method, but don't forget to call parent's `decode()`:: class CustomMessage(nlmsg): def decode(self): nlmsg.decode(self) ... # do some custom data tuning ''' self.offset = self.buf.tell() # decode the header if self.header is not None: try: self['header'].decode() # update length from header # it can not be less than 4 self.length = max(self['header']['length'], 4) save = self.buf.tell() self.buf.seek(self.offset) self.raw = self.buf.read(self.length) self.buf.seek(save) except Exception as e: raise NetlinkHeaderDecodeError(e) # decode the data try: if self.pack == 'struct': names = [] formats = [] for field in self.fields: names.append(field[0]) formats.append(field[1]) fields = ((','.join(names), ''.join(formats)), ) else: fields = self.fields for field in fields: name = field[0] fmt = field[1] # 's' and 'z' can be used only in connection with # length, encoded in the header if field[1] in ('s', 'z'): fmt = '%is' % (self.length - 4) size = struct.calcsize(fmt) raw = self.buf.read(size) actual_size = len(raw) # FIXME: adjust string size again if field[1] in ('s', 'z'): size = actual_size fmt = '%is' % (actual_size) if size == actual_size: value = struct.unpack(fmt, raw) if len(value) == 1: self[name] = value[0] # cut zero-byte from z-strings # 0x00 -- python3; '\0' -- python2 if field[1] == 'z' and self[name][-1] in (0x00, '\0'): self[name] = self[name][:-1] else: if self.pack == 'struct': names = name.split(',') values = list(value) for name in names: if name[0] != '_': self[name] = values.pop(0) else: self[name] = value else: # FIXME: log an error pass except Exception as e: raise NetlinkDataDecodeError(e) # decode NLA try: # align NLA chain start self.buf.seek(NLMSG_ALIGN(self.buf.tell())) # read NLA chain if self.nla_map: self.decode_nlas() except Exception as e: raise NetlinkNLADecodeError(e) if len(self['attrs']) == 0: del self['attrs'] if self['value'] is NotInitialized: del self['value'] def encode(self): ''' Encode the message into the binary buffer:: msg.encode() sock.send(msg.buf.getvalue()) If you want to customize the encoding process, override the method:: class CustomMessage(nlmsg): def encode(self): ... # do some custom data tuning nlmsg.encode(self) ''' init = self.buf.tell() diff = 0 # reserve space for the header if self.header is not None: self['header'].reserve() if self.getvalue() is not None: payload = b'' for i in self.fields: name = i[0] fmt = i[1] value = self[name] if fmt == 's': length = len(value) fmt = '%is' % (length) elif fmt == 'z': length = len(value) + 1 fmt = '%is' % (length) # in python3 we should force it if sys.version[0] == '3': if isinstance(value, str): value = bytes(value, 'utf-8') elif isinstance(value, float): value = int(value) elif sys.version[0] == '2': if isinstance(value, unicode): value = value.encode('utf-8') try: if fmt[-1] == 'x': payload += struct.pack(fmt) elif type(value) in (list, tuple, set): payload += struct.pack(fmt, *value) else: payload += struct.pack(fmt, value) except struct.error: logging.error(traceback.format_exc()) logging.error("error pack: %s %s %s" % (fmt, value, type(value))) raise diff = NLMSG_ALIGN(len(payload)) - len(payload) self.buf.write(payload) self.buf.write(b'\0' * diff) # write NLA chain if self.nla_map: diff = 0 self.encode_nlas() # calculate the size and write it if self.header is not None: self.update_length(init, diff) def update_length(self, start, diff=0): save = self.buf.tell() self['header']['length'] = save - start - diff self.buf.seek(start) self['header'].encode() self.buf.seek(save) def setvalue(self, value): if isinstance(value, dict): self.update(value) else: self['value'] = value self.value = value def get_encoded(self, attr, default=None): ''' Return the first encoded NLA by name ''' return self.get_attr(attr, default, 'encoded') def get_attr(self, attr, default=None, fmt='raw'): ''' Return the first attr by name or None ''' try: attrs = self.get_attrs(attr, fmt) except KeyError: return default if attrs: return attrs[0] else: return default def get_attrs(self, attr, fmt='raw'): ''' Return attrs by name ''' fmt_map = {'raw': 1, 'encoded': 2} return [i[fmt_map[fmt]] for i in self['attrs'] if i[0] == attr] def getvalue(self): ''' Atomic NLAs return their value in the 'value' field, not as a dictionary. Complex NLAs return whole dictionary. ''' if self.value != NotInitialized: # value decoded by custom decoder return self.value if 'value' in self and self['value'] != NotInitialized: # raw value got by generic decoder return self['value'] return self def register_nlas(self): ''' Convert 'nla_map' tuple into two dictionaries for mapping and reverse mapping of NLA types. ex: given:: nla_map = (('TCA_HTB_UNSPEC', 'none'), ('TCA_HTB_PARMS', 'htb_parms'), ('TCA_HTB_INIT', 'htb_glob')) creates:: t_nla_map = {0: (, 'TCA_HTB_UNSPEC'), 1: (, 'TCA_HTB_PARMS'), 2: (, 'TCA_HTB_INIT')} r_nla_map = {'TCA_HTB_UNSPEC': (, 0), 'TCA_HTB_PARMS': (, 1), 'TCA_HTB_INIT': (, 2)} ''' # clean up NLA mappings self.t_nla_map = {} self.r_nla_map = {} # work only on non-empty mappings if not self.nla_map: return # detect, whether we have pre-defined keys if len(self.nla_map[0]) == 2: # create enumeration nla_types = enumerate((i[0] for i in self.nla_map)) # that's a little bit tricky, but to reduce # the required amount of code in modules, we have # to jump over the head zipped = [(k[1][0], k[0][0], k[0][1]) for k in zip(self.nla_map, nla_types)] else: zipped = self.nla_map for (key, name, nla_class) in zipped: # lookup NLA class nla_class = getattr(self, nla_class) # update mappings self.t_nla_map[key] = (nla_class, name) self.r_nla_map[name] = (nla_class, key) def encode_nlas(self): ''' Encode the NLA chain. Should not be called manually, since it is called from `encode()` routine. ''' for i in self['attrs']: if i[0] in self.r_nla_map: msg_class = self.r_nla_map[i[0]][0] msg_type = self.r_nla_map[i[0]][1] # is it a class or a function? if isinstance(msg_class, types.MethodType): # if it is a function -- use it to get the class msg_class = msg_class() # encode NLA nla = msg_class(self.buf, parent=self) nla['header']['type'] = msg_type nla.setvalue(i[1]) try: nla.encode() except: raise else: if len(i) == 2: i.append(nla) elif len(i) == 3: i[2] = nla def decode_nlas(self): ''' Decode the NLA chain. Should not be called manually, since it is called from `decode()` routine. ''' while self.buf.tell() < (self.offset + self.length): init = self.buf.tell() nla = None # pick the length and the type (length, msg_type) = struct.unpack('HH', self.buf.read(4)) # rewind to the beginning self.buf.seek(init) length = min(max(length, 4), (self.length - self.buf.tell() + self.offset)) # we have a mapping for this NLA if msg_type in self.t_nla_map: # get the class msg_class = self.t_nla_map[msg_type][0] # is it a class or a function? if isinstance(msg_class, types.MethodType): # if it is a function -- use it to get the class msg_class = msg_class(buf=self.buf, length=length) # and the name msg_name = self.t_nla_map[msg_type][1] # decode NLA nla = msg_class(self.buf, length, self, debug=self.debug) try: nla.decode() except: # FIXME self.buf.seek(init) msg_name = 'UNKNOWN' msg_value = hexdump(self.buf.read(length)) else: msg_value = nla.getvalue() self['attrs'].append([msg_name, msg_value]) # fix the offset self.buf.seek(init + NLMSG_ALIGN(length)) class nla_header(nlmsg_base): ''' The NLA header structure: uin16 length and uint16 type. ''' fields = (('length', 'H'), ('type', 'H')) class nla_base(nlmsg_base): ''' The NLA base class. Use `nla_header` class as the header. ''' header = nla_header class nlmsg_header(nlmsg_base): ''' Common netlink message header ''' fields = (('length', 'I'), ('type', 'H'), ('flags', 'H'), ('sequence_number', 'I'), ('pid', 'I')) class nlmsg_atoms(nlmsg_base): ''' A collection of base NLA types ''' class none(nla_base): ''' 'none' type is used to skip decoding of NLA. You can also use 'hex' type to dump NLA's content. ''' def decode(self): nla_base.decode(self) self.value = None class uint8(nla_base): fields = [('value', 'B')] class uint16(nla_base): fields = [('value', 'H')] class uint32(nla_base): fields = [('value', 'I')] class uint64(nla_base): fields = [('value', 'Q')] class be8(nla_base): fields = [('value', '>B')] class be16(nla_base): fields = [('value', '>H')] class be32(nla_base): fields = [('value', '>I')] class ipXaddr(nla_base): fields = [('value', 's')] family = None def encode(self): self['value'] = socket.inet_pton(self.family, self.value) nla_base.encode(self) def decode(self): nla_base.decode(self) self.value = socket.inet_ntop(self.family, self['value']) class ip4addr(ipXaddr): ''' Explicit IPv4 address type class. ''' family = socket.AF_INET class ip6addr(ipXaddr): ''' Explicit IPv6 address type class. ''' family = socket.AF_INET6 class ipaddr(nla_base): ''' This class is used to decode IP addresses according to the family. Socket library currently supports only two families, AF_INET and AF_INET6. We do not specify here the string size, it will be calculated in runtime. ''' fields = [('value', 's')] def encode(self): self['value'] = socket.inet_pton(self.parent['family'], self.value) nla_base.encode(self) def decode(self): nla_base.decode(self) self.value = socket.inet_ntop(self.parent['family'], self['value']) class l2addr(nla_base): ''' Decode MAC address. ''' fields = [('value', '=6s')] def encode(self): self['value'] = struct.pack('BBBBBB', *[int(i, 16) for i in self.value.split(':')]) nla_base.encode(self) def decode(self): nla_base.decode(self) self.value = ':'.join('%02x' % (i) for i in struct.unpack('BBBBBB', self['value'])) class hex(nla_base): ''' Represent NLA's content with header as hex string. ''' fields = [('value', 's')] def decode(self): nla_base.decode(self) self.value = hexdump(self['value']) class cdata(nla_base): ''' Binary data ''' fields = [('value', 's')] class asciiz(nla_base): ''' Zero-terminated string. ''' # FIXME: move z-string hacks from general decode here? fields = [('value', 'z')] def encode(self): if isinstance(self['value'], str) and sys.version[0] == '3': self['value'] = bytes(self['value'], 'utf-8') nla_base.encode(self) def decode(self): nla_base.decode(self) try: assert sys.version[0] == '3' self.value = self['value'].decode('utf-8') except (AssertionError, UnicodeDecodeError): self.value = self['value'] class nla(nla_base, nlmsg_atoms): ''' Main NLA class ''' def decode(self): nla_base.decode(self) if not self.debug: del self['header'] class nlmsg(nlmsg_atoms): ''' Main netlink message class ''' header = nlmsg_header class genlmsg(nlmsg): ''' Generic netlink message ''' fields = (('cmd', 'B'), ('version', 'B'), ('reserved', 'H')) class ctrlmsg(genlmsg): ''' Netlink control message ''' # FIXME: to be extended nla_map = (('CTRL_ATTR_UNSPEC', 'none'), ('CTRL_ATTR_FAMILY_ID', 'uint16'), ('CTRL_ATTR_FAMILY_NAME', 'asciiz'), ('CTRL_ATTR_VERSION', 'hex'), ('CTRL_ATTR_HDRSIZE', 'hex'), ('CTRL_ATTR_MAXATTR', 'hex'), ('CTRL_ATTR_OPS', 'hex'), ('CTRL_ATTR_MCAST_GROUPS', 'hex')) pyroute2-0.3.5/pyroute2/netlink/nlsocket.py0000664000175000017500000007024612471117116020671 0ustar peetpeet00000000000000''' Base netlink socket and marshal =============================== All the netlink providers are derived from the socket class, so they provide normal socket API, including `getsockopt()`, `setsockopt()`, they can be used in poll/select I/O loops etc. asynchronous I/O ---------------- To run async reader thread, one should call `NetlinkSocket.bind(async=True)`. In that case a background thread will be launched. The thread will automatically collect all the messages and store into a userspace buffer. .. note:: There is no need to turn on async I/O, if you don't plan to receive broadcast messages. ENOBUF and async I/O -------------------- When Netlink messages arrive faster than a program reads then from the socket, the messages overflow the socket buffer and one gets ENOBUF on `recv()`:: ... self.recv(bufsize) error: [Errno 105] No buffer space available One way to avoid ENOBUF, is to use async I/O. Then the library not only reads and buffers all the messages, but also re-prioritizes threads. Suppressing the parser activity, the library increases the response delay, but spares CPU to read and enqueue arriving messages as fast, as it is possible. With logging level DEBUG you can notice messages, that the library started to calm down the parser thread:: DEBUG:root:Packet burst: the reader thread priority is increased, beware of delays on netlink calls Counters: delta=25 qsize=25 delay=0.1 This state requires no immediate action, but just some more attention. When the delay between messages on the parser thread exceeds 1 second, DEBUG messages become WARNING ones:: WARNING:root:Packet burst: the reader thread priority is increased, beware of delays on netlink calls Counters: delta=2525 qsize=213536 delay=3 This state means, that almost all the CPU resources are dedicated to the reader thread. It doesn't mean, that the reader thread consumes 100% CPU -- it means, that the CPU is reserved for the case of more intensive bursts. The library will return to the normal state only when the broadcast storm will be over, and then the CPU will be 100% loaded with the parser for some time, when it will process all the messages queued so far. when async I/O doesn't help --------------------------- Sometimes, even turning async I/O doesn't fix ENOBUF. Mostly it means, that in this particular case the Python performance is not enough even to read and store the raw data from the socket. There is no workaround for such cases, except of using something *not* Python-based. One can still play around with SO_RCVBUF socket option, but it doesn't help much. So keep it in mind, and if you expect massive broadcast Netlink storms, perform stress testing prior to deploy a solution in the production. classes ------- ''' import os import time import struct import logging import traceback import threading from socket import AF_NETLINK from socket import SOCK_DGRAM from socket import MSG_PEEK from socket import SOL_SOCKET from socket import SO_RCVBUF from socket import SO_SNDBUF from socket import error as SocketError from pyroute2.config import SocketBase from pyroute2.common import AddrPool from pyroute2.common import DEFAULT_RCVBUF from pyroute2.netlink import nlmsg from pyroute2.netlink import mtypes from pyroute2.netlink import NetlinkError from pyroute2.netlink import NetlinkDecodeError from pyroute2.netlink import NetlinkHeaderDecodeError from pyroute2.netlink import NLMSG_ERROR from pyroute2.netlink import NLMSG_DONE from pyroute2.netlink import NETLINK_GENERIC from pyroute2.netlink import NLM_F_DUMP from pyroute2.netlink import NLM_F_MULTI from pyroute2.netlink import NLM_F_REQUEST try: from Queue import Queue except ImportError: from queue import Queue class Marshal(object): ''' Generic marshalling class ''' msg_map = {} debug = False def __init__(self): self.lock = threading.Lock() # one marshal instance can be used to parse one # message at once self.msg_map = self.msg_map or {} self.defragmentation = {} def parse(self, data): ''' Parse string data. At this moment all transport, except of the native Netlink is deprecated in this library, so we should not support any defragmentation on that level ''' offset = 0 result = [] while offset < len(data): # pick type and length (length, msg_type) = struct.unpack('IH', data[offset:offset+6]) error = None if msg_type == NLMSG_ERROR: code = abs(struct.unpack('i', data[offset+16:offset+20])[0]) if code > 0: error = NetlinkError(code) msg_class = self.msg_map.get(msg_type, nlmsg) msg = msg_class(data[offset:offset+length], debug=self.debug) try: msg.decode() msg['header']['error'] = error # try to decode encapsulated error message if error is not None: enc_type = struct.unpack('H', msg.raw[24:26])[0] enc_class = self.msg_map.get(enc_type, nlmsg) enc = enc_class(msg.raw[20:]) enc.decode() msg['header']['errmsg'] = enc except NetlinkHeaderDecodeError as e: # in the case of header decoding error, # create an empty message msg = nlmsg() msg['header']['error'] = e except NetlinkDecodeError as e: msg['header']['error'] = e mtype = msg['header'].get('type', None) if mtype in (1, 2, 3, 4): msg['event'] = mtypes.get(mtype, 'none') self.fix_message(msg) offset += msg.length result.append(msg) return result def fix_message(self, msg): pass # 8<----------------------------------------------------------- # Singleton, containing possible modifiers to the NetlinkSocket # bind() call. # # Normally, you can open only one netlink connection for one # process, but there is a hack. Current PID_MAX_LIMIT is 2^22, # so we can use the rest to midify pid field. # # See also libnl library, lib/socket.c:generate_local_port() sockets = AddrPool(minaddr=0x0, maxaddr=0x3ff, reverse=True) # 8<----------------------------------------------------------- class LockProxy(object): def __init__(self, factory, key): self.factory = factory self.refcount = 0 self.key = key self.internal = threading.Lock() self.lock = factory.klass() def acquire(self, *argv, **kwarg): with self.internal: self.refcount += 1 return self.lock.acquire() def release(self): with self.internal: self.refcount -= 1 if (self.refcount == 0) and (self.key != 0): try: del self.factory.locks[self.key] except KeyError: pass return self.lock.release() def __enter__(self): self.acquire() def __exit__(self, exc_type, exc_value, traceback): self.release() class LockFactory(object): def __init__(self, klass=threading.RLock): self.klass = klass self.locks = {0: LockProxy(self, 0)} def __enter__(self): self.locks[0].acquire() def __exit__(self, exc_type, exc_value, traceback): self.locks[0].release() def __getitem__(self, key): if key is None: key = 0 if key not in self.locks: self.locks[key] = LockProxy(self, key) return self.locks[key] def __delitem__(self, key): del self.locks[key] class NetlinkMixin(object): ''' Generic netlink socket ''' def __init__(self, family=NETLINK_GENERIC, port=None, pid=None): super(NetlinkMixin, self).__init__(AF_NETLINK, SOCK_DGRAM, family) global sockets self.recv_plugin = self.recv_plugin_init # 8<----------------------------------------- # PID init is here only for compatibility, # later it will be completely moved to bind() self.addr_pool = AddrPool(minaddr=0xff) self.epid = None self.port = 0 self.fixed = True self.backlog = {0: []} self.monitor = False self.callbacks = [] # [(predicate, callback, args), ...] self.pthread = None self.backlog_lock = threading.Lock() self.read_lock = threading.Lock() self.change_master = threading.Event() self.lock = LockFactory() self.buffer_queue = Queue() self.qsize = 0 self.log = [] self.get_timeout = 3 self.get_timeout_exception = None if pid is None: self.pid = os.getpid() & 0x3fffff self.port = port self.fixed = self.port is not None elif pid == 0: self.pid = os.getpid() else: self.pid = pid # 8<----------------------------------------- self.groups = 0 self.marshal = Marshal() # 8<----------------------------------------- # Set default sockopts self.setsockopt(SOL_SOCKET, SO_SNDBUF, 32768) self.setsockopt(SOL_SOCKET, SO_RCVBUF, 1024 * 1024) def release(self): logging.warning("The `release()` call is deprecated") logging.warning("Use `close()` instead") self.close() def register_callback(self, callback, predicate=lambda x: True, args=None): ''' Register a callback to run on a message arrival. Callback is the function that will be called with the message as the first argument. Predicate is the optional callable object, that returns True or False. Upon True, the callback will be called. Upon False it will not. Args is a list or tuple of arguments. Simplest example, assume ipr is the IPRoute() instance:: # create a simplest callback that will print messages def cb(msg): print(msg) # register callback for any message: ipr.register_callback(cb) More complex example, with filtering:: # Set object's attribute after the message key def cb(msg, obj): obj.some_attr = msg["some key"] # Register the callback only for the loopback device, index 1: ipr.register_callback(cb, lambda x: x.get('index', None) == 1, (self, )) Please note: you do **not** need to register the default 0 queue to invoke callbacks on broadcast messages. Callbacks are iterated **before** messages get enqueued. ''' if args is None: args = [] self.callbacks.append((predicate, callback, args)) def unregister_callback(self, callback): ''' Remove the first reference to the function from the callback register ''' cb = tuple(self.callbacks) for cr in cb: if cr[1] == callback: self.callbacks.pop(cb.index(cr)) return def register_policy(self, policy, msg_class=None): ''' Register netlink encoding/decoding policy. Can be specified in two ways: `nlsocket.register_policy(MSG_ID, msg_class)` to register one particular rule, or `nlsocket.register_policy({MSG_ID1: msg_class})` to register several rules at once. E.g.:: policy = {RTM_NEWLINK: ifinfmsg, RTM_DELLINK: ifinfmsg, RTM_NEWADDR: ifaddrmsg, RTM_DELADDR: ifaddrmsg} nlsocket.register_policy(policy) One can call `register_policy()` as many times, as one want to -- it will just extend the current policy scheme, not replace it. ''' if isinstance(policy, int) and msg_class is not None: policy = {policy: msg_class} assert isinstance(policy, dict) for key in policy: self.marshal.msg_map[key] = policy[key] return self.marshal.msg_map def unregister_policy(self, policy): ''' Unregister policy. Policy can be: * int -- then it will just remove one policy * list or tuple of ints -- remove all given * dict -- remove policies by keys from dict In the last case the routine will ignore dict values, it is implemented so just to make it compatible with `get_policy_map()` return value. ''' if isinstance(policy, int): policy = [policy] elif isinstance(policy, dict): policy = list(policy) assert isinstance(policy, (tuple, list, set)) for key in policy: del self.marshal.msg_map[key] return self.marshal.msg_map def get_policy_map(self, policy=None): ''' Return policy for a given message type or for all message types. Policy parameter can be either int, or a list of ints. Always return dictionary. ''' if policy is None: return self.marshal.msg_map if isinstance(policy, int): policy = [policy] assert isinstance(policy, (list, tuple, set)) ret = {} for key in policy: ret[key] = self.marshal.msg_map[key] return ret def bind(self, groups=0, pid=None, async=False): ''' Bind the socket to given multicast groups, using given pid. * If pid is None, use automatic port allocation * If pid == 0, use process' pid * If pid == , use the value instead of pid ''' if pid is not None: self.port = 0 self.fixed = True self.pid = pid or os.getpid() self.groups = groups # if we have pre-defined port, use it strictly if self.fixed: self.epid = self.pid + (self.port << 22) super(NetlinkMixin, self).bind((self.epid, self.groups)) else: # if we have no pre-defined port, scan all the # range till the first available port for i in range(1024): try: self.port = sockets.alloc() self.epid = self.pid + (self.port << 22) super(NetlinkMixin, self).bind((self.epid, self.groups)) # if we're here, bind() done successfully, just exit break except SocketError as e: # pass occupied sockets, raise other exceptions if e.errno != 98: raise else: # raise "address in use" -- to be compatible raise SocketError(98, 'Address already in use') # all is OK till now, so start async recv, if we need if async: self._stop = False self.recv_plugin = self.recv_plugin_queue self.pthread = threading.Thread(target=self.async_recv) self.pthread.setDaemon(True) self.pthread.start() def recv_plugin_init(self, *argv, **kwarg): # # One-shot method # # Substitutes itself with the current recv() # pointer. # # It is required since child classes can # initialize recv() in the init() # self.recv_plugin = self.recv return self.recv(*argv, **kwarg) def recv_plugin_queue(self, *argv, **kwarg): data = self.buffer_queue.get() if isinstance(data, Exception): raise data else: return data def async_recv(self): while not self._stop: try: self.buffer_queue.put(self.recv(1024 * 1024)) except Exception as e: self.buffer_queue.put(e) def put(self, msg, msg_type, msg_flags=NLM_F_REQUEST, addr=(0, 0), msg_seq=0, msg_pid=None): ''' Construct a message from a dictionary and send it to the socket. Parameters: * msg -- the message in the dictionary format * msg_type -- the message type * msg_flags -- the message flags to use in the request * addr -- `sendto()` addr, default `(0, 0)` * msg_seq -- sequence number to use * msg_pid -- pid to use, if `None` -- use os.getpid() Example:: s = IPRSocket() s.bind() s.put({'index': 1}, RTM_GETLINK) s.get() s.close() Please notice, that the return value of `s.get()` can be not the result of `s.put()`, but any broadcast message. To fix that, use `msg_seq` -- the response must contain the same `msg['header']['sequence_number']` value. ''' if msg_seq != 0: self.lock[msg_seq].acquire() try: if msg_seq not in self.backlog: self.backlog[msg_seq] = [] if not isinstance(msg, nlmsg): msg_class = self.marshal.msg_map[msg_type] msg = msg_class(msg) if msg_pid is None: msg_pid = os.getpid() msg['header']['type'] = msg_type msg['header']['flags'] = msg_flags msg['header']['sequence_number'] = msg_seq msg['header']['pid'] = msg_pid msg.encode() self.sendto(msg.buf.getvalue(), addr) except: raise finally: if msg_seq != 0: self.lock[msg_seq].release() def get(self, bufsize=DEFAULT_RCVBUF, msg_seq=0, terminate=None): ''' Get parsed messages list. If `msg_seq` is given, return only messages with that `msg['header']['sequence_number']`, saving all other messages into `self.backlog`. The routine is thread-safe. The `bufsize` parameter can be: * -1: bufsize will be calculated from the first 4 bytes of the network data * 0: bufsize will be calculated from SO_RCVBUF sockopt * int >= 0: just a bufsize ''' ctime = time.time() with self.lock[msg_seq]: if bufsize == -1: # get bufsize from the network data bufsize = struct.unpack("I", self.recv(4, MSG_PEEK))[0] elif bufsize == 0: # get bufsize from SO_RCVBUF bufsize = self.getsockopt(SOL_SOCKET, SO_RCVBUF) // 2 ret = [] enough = False while not enough: # 8<----------------------------------------------------------- # # This stage changes the backlog, so use mutex to # prevent side changes self.backlog_lock.acquire() ## # Stage 1. BEGIN # # 8<----------------------------------------------------------- # # Check backlog and return already collected # messages. # if msg_seq == 0 and self.backlog[0]: # Zero queue. # # Load the backlog, if there is valid # content in it ret.extend(self.backlog[0]) self.backlog[0] = [] # And just exit self.backlog_lock.release() break elif self.backlog.get(msg_seq, None): # Any other msg_seq. # # Collect messages up to the terminator. # Terminator conditions: # * NLMSG_ERROR != 0 # * NLMSG_DONE # * terminate() function (if defined) # * not NLM_F_MULTI # # Please note, that if terminator not occured, # more `recv()` rounds CAN be required. for msg in tuple(self.backlog[msg_seq]): # Drop the message from the backlog, if any self.backlog[msg_seq].remove(msg) # If there is an error, raise exception if msg['header'].get('error', None) is not None: self.backlog[0].extend(self.backlog[msg_seq]) del self.backlog[msg_seq] # The loop is done self.backlog_lock.release() raise msg['header']['error'] # If it is the terminator message, say "enough" # and requeue all the rest into Zero queue if (msg['header']['type'] == NLMSG_DONE) or \ (terminate is not None and terminate(msg)): # The loop is done enough = True # If it is just a normal message, append it to # the response if not enough: ret.append(msg) # But finish the loop on single messages if not msg['header']['flags'] & NLM_F_MULTI: # but not multi -- so end the loop enough = True # Enough is enough, requeue the rest and delete # our backlog if enough: self.backlog[0].extend(self.backlog[msg_seq]) del self.backlog[msg_seq] break # Next iteration self.backlog_lock.release() else: # Stage 1. END # # 8<------------------------------------------------------- # # Stage 2. BEGIN # # 8<------------------------------------------------------- # # Receive the data from the socket and put the messages # into the backlog # self.backlog_lock.release() ## # # Control the timeout. We should not be within the # function more than TIMEOUT seconds. All the locks # MUST be released here. # if time.time() - ctime > self.get_timeout: if self.get_timeout_exception: raise self.get_timeout_exception() else: return ret # if self.read_lock.acquire(False): self.change_master.clear() # If the socket is free to read from, occupy # it and wait for the data # # This is a time consuming process, so all the # locks, except the read lock must be released data = self.recv_plugin(bufsize) # Parse data msgs = self.marshal.parse(data) # Reset ctime -- timeout should be measured # for every turn separately ctime = time.time() # current = self.buffer_queue.qsize() delta = current - self.qsize if delta > 10: delay = min(3, max(0.1, float(current) / 60000)) message = ("Packet burst: the reader thread " "priority is increased, beware of " "delays on netlink calls\n\tCounters: " "delta=%s qsize=%s delay=%s " % (delta, current, delay)) if delay < 1: logging.debug(message) else: logging.warning(message) time.sleep(delay) self.qsize = current # We've got the data, lock the backlog again self.backlog_lock.acquire() for msg in msgs: seq = msg['header']['sequence_number'] if seq not in self.backlog: if msg['header']['type'] == NLMSG_ERROR: # Drop orphaned NLMSG_ERROR messages continue seq = 0 # 8<----------------------------------------------- # Callbacks section for cr in self.callbacks: try: if cr[0](msg): cr[1](msg, *cr[2]) except: logging.warning("Callback fail: %s" % (cr)) logging.warning(traceback.format_exc()) # 8<----------------------------------------------- self.backlog[seq].append(msg) # Monitor mode: if self.monitor and seq != 0: self.backlog[0].append(msg) # We finished with the backlog, so release the lock self.backlog_lock.release() # Now wake up other threads self.change_master.set() # Finally, release the read lock: all data processed self.read_lock.release() else: # If the socket is occupied and there is still no # data for us, wait for the next master change or # for a timeout self.change_master.wait(1) # 8<------------------------------------------------------- # # Stage 2. END # # 8<------------------------------------------------------- return ret def nlm_request(self, msg, msg_type, msg_flags=NLM_F_REQUEST | NLM_F_DUMP, terminate=None): msg_seq = self.addr_pool.alloc() with self.lock[msg_seq]: try: self.put(msg, msg_type, msg_flags, msg_seq=msg_seq) ret = self.get(msg_seq=msg_seq, terminate=terminate) return ret except: raise finally: # Ban this msg_seq for 0xff rounds # # It's a long story. Modern kernels for RTM_SET.* operations # always return NLMSG_ERROR(0) == success, even not setting # NLM_F_MULTY flag on other response messages and thus w/o # any NLMSG_DONE. So, how to detect the response end? One # can not rely on NLMSG_ERROR on old kernels, but we have to # support them too. Ty, we just ban msg_seq for several rounds, # and NLMSG_ERROR, being received, will become orphaned and # just dropped. # # Hack, but true. self.addr_pool.free(msg_seq, ban=0xff) def close(self): ''' Correctly close the socket and free all resources. ''' global sockets if self.pthread is not None: self._stop = True if self.epid is not None: assert self.port is not None if not self.fixed: sockets.free(self.port) self.epid = None super(NetlinkMixin, self).close() class NetlinkSocket(NetlinkMixin, SocketBase): pass pyroute2-0.3.5/pyroute2/netlink/generic/0000775000175000017500000000000012474420674020111 5ustar peetpeet00000000000000pyroute2-0.3.5/pyroute2/netlink/generic/__init__.py0000664000175000017500000000343112471117116022212 0ustar peetpeet00000000000000''' Generic netlink =============== Describe ''' from pyroute2.netlink import CTRL_CMD_GETFAMILY from pyroute2.netlink import GENL_ID_CTRL from pyroute2.netlink import NLM_F_REQUEST from pyroute2.netlink import ctrlmsg from pyroute2.netlink.nlsocket import NetlinkSocket class GenericNetlinkSocket(NetlinkSocket): ''' Low-level socket interface. Provides all the usual socket does, can be used in poll/select, doesn't create any implicit threads. Provides two additional methods: * get_protocol_id() -- resolve generic netlink proto * get() -- receive and parse netlink messages ''' def bind(self, proto, msg_class, groups=0, pid=0, async=False): ''' Bind the socket and performs generic netlink proto lookup. The `proto` parameter is a string, like "TASKSTATS", `msg_class` is a class to parse messages with. ''' NetlinkSocket.bind(self, groups, pid, async) self.marshal.msg_map[GENL_ID_CTRL] = ctrlmsg self.prid = self.get_protocol_id(proto) self.marshal.msg_map[self.prid] = msg_class def get_protocol_id(self, proto): ''' Resolve generic netlink protocol -- takes a string as the only parameter, return integer proto ID ''' msg = ctrlmsg() msg['cmd'] = CTRL_CMD_GETFAMILY msg['version'] = 1 msg['attrs'].append(['CTRL_ATTR_FAMILY_NAME', proto]) msg['header']['type'] = GENL_ID_CTRL msg['header']['flags'] = NLM_F_REQUEST msg['header']['pid'] = self.pid msg.encode() self.sendto(msg.buf.getvalue(), (0, 0)) msg = self.get()[0] err = msg['header'].get('error', None) if err is not None: raise err return msg.get_attr('CTRL_ATTR_FAMILY_ID') pyroute2-0.3.5/pyroute2/netlink/nfnetlink/0000775000175000017500000000000012474420674020465 5ustar peetpeet00000000000000pyroute2-0.3.5/pyroute2/netlink/nfnetlink/__init__.py0000664000175000017500000000040212432725447022572 0ustar peetpeet00000000000000''' Nfnetlink ========= The support of nfnetlink families is now at the very beginning. So there is no public exports yet, but you can review the code. Work is in progress, stay tuned. nf-queue ++++++++ Netfilter protocol for NFQUEUE iptables target. ''' pyroute2-0.3.5/pyroute2/netlink/nfnetlink/nfqueue.py0000664000175000017500000000314512441630373022503 0ustar peetpeet00000000000000from pyroute2.netlink import NLM_F_REQUEST from pyroute2.netlink import NETLINK_NETFILTER from pyroute2.netlink import nlmsg from pyroute2.netlink import nla from pyroute2.netlink.client import Netlink from pyroute2.netlink.nlsocket import Marshal NFNL_SUBSYS_NONE = 0 NFNL_SUBSYS_CTNETLINK = 1 NFNL_SUBSYS_CTNETLINK_EXP = 2 NFNL_SUBSYS_QUEUE = 3 NFNL_SUBSYS_ULOG = 4 NFNL_SUBSYS_OSF = 5 NFNL_SUBSYS_IPSET = 6 NFNL_SUBSYS_COUNT = 7 # constants NFQNL_MSG_PACKET = 0 NFQNL_MSG_VERDICT = 1 NFQNL_MSG_CONFIG = 2 # verdict types NF_DROP = 0 NF_ACCEPT = 1 NF_STOLEN = 2 NF_QUEUE = 3 NF_REPEAT = 4 NF_STOP = 5 class nfq_packet_msg(nlmsg): pass class nfq_verdict_msg(nlmsg): pass class nfq_config_msg(nlmsg): nla_map = (('NFQA_CFG_UNSPEC', 'none'), ('NFQA_CFG_CMD', 'nfqa_cfg_cmd'), ('NFQA_CFG_PARAMS', 'none')) class nfqa_cfg_cmd(nla): fields = (('command', 'B'), ('__pad', '1x'), ('pf', 'H')) class MarshalNFQ(Marshal): msg_map = {NFQNL_MSG_PACKET: nfq_packet_msg, NFQNL_MSG_VERDICT: nfq_verdict_msg, NFQNL_MSG_CONFIG: nfq_config_msg} class NFQ(Netlink): family = NETLINK_NETFILTER marshal = MarshalNFQ def __init__(self): Netlink.__init__(self, pid=0) def config(self): msg = nfq_config_msg() msg['header']['flags'] = NLM_F_REQUEST msg['header']['type'] = (3 << 8) | NFQNL_MSG_CONFIG msg['attrs'] = [['NFQA_CFG_CMD', {'command': 1, 'pf': 1}]] msg.encode() self.push(msg.buf.getvalue()) pyroute2-0.3.5/pyroute2/netlink/rtnl/0000775000175000017500000000000012474420674017454 5ustar peetpeet00000000000000pyroute2-0.3.5/pyroute2/netlink/rtnl/req.py0000664000175000017500000001253412455770277020630 0ustar peetpeet00000000000000from socket import AF_INET6 from pyroute2.common import basestring from pyroute2.netlink.rtnl.rtmsg import rtmsg class IPRequest(dict): def __init__(self, obj=None): dict.__init__(self) if obj is not None: self.update(obj) def update(self, obj): for key in obj: if obj[key] is not None: self[key] = obj[key] class IPRouteRequest(IPRequest): ''' Utility class, that converts human-readable dictionary into RTNL route request. ''' def __setitem__(self, key, value): # fix family if isinstance(value, basestring) and value.find(':') >= 0: self['family'] = AF_INET6 # work on the rest if key == 'dst': if value != 'default': value = value.split('/') if len(value) == 1: dst = value[0] mask = 0 elif len(value) == 2: dst = value[0] mask = int(value[1]) else: raise ValueError('wrong destination') dict.__setitem__(self, 'dst', dst) dict.__setitem__(self, 'dst_len', mask) elif key == 'metrics': ret = {'attrs': []} for name in value: rtax = rtmsg.metrics.name2nla(name) ret['attrs'].append([rtax, value[name]]) dict.__setitem__(self, 'metrics', ret) else: dict.__setitem__(self, key, value) class CBRequest(IPRequest): ''' FIXME ''' commands = None msg = None def __init__(self, *argv, **kwarg): self['commands'] = {'attrs': []} def __setitem__(self, key, value): if value is None: return if key in self.commands: self['commands']['attrs'].\ append([self.msg.name2nla(key), value]) else: dict.__setitem__(self, key, value) class IPLinkRequest(IPRequest): ''' Utility class, that converts human-readable dictionary into RTNL link request. ''' blacklist = ['carrier', 'carrier_changes'] def __init__(self, *argv, **kwarg): self.deferred = [] IPRequest.__init__(self, *argv, **kwarg) if 'index' not in self: self['index'] = 0 def __setitem__(self, key, value): # ignore blacklisted attributes if key in self.blacklist: return # there must be no "None" values in the request if value is None: return # all the values must be in ascii try: if isinstance(value, unicode): value = value.encode('ascii') except NameError: pass # set up specific keys if key == 'kind': self['IFLA_LINKINFO'] = {'attrs': []} linkinfo = self['IFLA_LINKINFO']['attrs'] linkinfo.append(['IFLA_INFO_KIND', value]) if value in ('vlan', 'bond', 'tuntap', 'veth'): linkinfo.append(['IFLA_INFO_DATA', {'attrs': []}]) elif key == 'vlan_id': nla = ['IFLA_VLAN_ID', value] # FIXME: we need to replace, not add self.defer_nla(nla, ('IFLA_LINKINFO', 'IFLA_INFO_DATA'), lambda x: x.get('kind', None) == 'vlan') elif key == 'gid': nla = ['IFTUN_UID', value] self.defer_nla(nla, ('IFLA_LINKINFO', 'IFLA_INFO_DATA'), lambda x: x.get('kind', None) == 'tuntap') elif key == 'uid': nla = ['IFTUN_UID', value] self.defer_nla(nla, ('IFLA_LINKINFO', 'IFLA_INFO_DATA'), lambda x: x.get('kind', None) == 'tuntap') elif key == 'mode': nla = ['IFTUN_MODE', value] self.defer_nla(nla, ('IFLA_LINKINFO', 'IFLA_INFO_DATA'), lambda x: x.get('kind', None) == 'tuntap') nla = ['IFLA_BOND_MODE', value] self.defer_nla(nla, ('IFLA_LINKINFO', 'IFLA_INFO_DATA'), lambda x: x.get('kind', None) == 'bond') elif key == 'ifr': nla = ['IFTUN_IFR', value] self.defer_nla(nla, ('IFLA_LINKINFO', 'IFLA_INFO_DATA'), lambda x: x.get('kind', None) == 'tuntap') elif key == 'peer': nla = ['VETH_INFO_PEER', {'attrs': [['IFLA_IFNAME', value]]}] self.defer_nla(nla, ('IFLA_LINKINFO', 'IFLA_INFO_DATA'), lambda x: x.get('kind', None) == 'veth') dict.__setitem__(self, key, value) if self.deferred: self.flush_deferred() def flush_deferred(self): deferred = [] for nla, path, predicate in self.deferred: if predicate(self): self.append_nla(nla, path) else: deferred.append((nla, path, predicate)) self.deferred = deferred def append_nla(self, nla, path): pwd = self for step in path: if step in pwd: pwd = pwd[step] else: pwd = [x[1] for x in pwd['attrs'] if x[0] == step][0]['attrs'] pwd.append(nla) def defer_nla(self, nla, path, predicate): self.deferred.append((nla, path, predicate)) self.flush_deferred() pyroute2-0.3.5/pyroute2/netlink/rtnl/__init__.py0000664000175000017500000002463112467374327021600 0ustar peetpeet00000000000000''' RTNetlink: network setup ======================== RTNL is a netlink protocol, used to get and set information about different network objects -- addresses, routes, interfaces etc. RTNL protocol-specific data in messages depends on the object type. E.g., complete packet with the interface address information:: nlmsg header: + uint32 length + uint16 type + uint16 flags + uint32 sequence number + uint32 pid ifaddrmsg structure: + unsigned char ifa_family + unsigned char ifa_prefixlen + unsigned char ifa_flags + unsigned char ifa_scope + uint32 ifa_index [ optional NLA tree ] NLA for this kind of packets can be of type IFA_ADDRESS, IFA_LOCAL etc. -- please refer to the corresponding source. Other objects types require different structures, sometimes really complex. All these structures are described in sources. --------------------------- Module contents: ''' from pyroute2.proxy import NetlinkProxy from pyroute2.common import map_namespace from pyroute2.common import ANCIENT from pyroute2.netlink import NETLINK_ROUTE from pyroute2.netlink.nlsocket import Marshal from pyroute2.netlink.nlsocket import NetlinkSocket from pyroute2.netlink.rtnl.tcmsg import tcmsg from pyroute2.netlink.rtnl.rtmsg import rtmsg from pyroute2.netlink.rtnl.ndmsg import ndmsg from pyroute2.netlink.rtnl.dhcpmsg import dhcpmsg from pyroute2.netlink.rtnl.ifinfmsg import ifinfmsg from pyroute2.netlink.rtnl.ifinfmsg import proxy_newlink from pyroute2.netlink.rtnl.ifinfmsg import proxy_setlink from pyroute2.netlink.rtnl.ifinfmsg import proxy_dellink from pyroute2.netlink.rtnl.ifinfmsg import proxy_linkinfo from pyroute2.netlink.rtnl.ifaddrmsg import ifaddrmsg # RTnetlink multicast groups RTNLGRP_NONE = 0x0 RTNLGRP_LINK = 0x1 RTNLGRP_NOTIFY = 0x2 RTNLGRP_NEIGH = 0x4 RTNLGRP_TC = 0x8 RTNLGRP_IPV4_IFADDR = 0x10 RTNLGRP_IPV4_MROUTE = 0x20 RTNLGRP_IPV4_ROUTE = 0x40 RTNLGRP_IPV4_RULE = 0x80 RTNLGRP_IPV6_IFADDR = 0x100 RTNLGRP_IPV6_MROUTE = 0x200 RTNLGRP_IPV6_ROUTE = 0x400 RTNLGRP_IPV6_IFINFO = 0x800 RTNLGRP_DECnet_IFADDR = 0x1000 RTNLGRP_NOP2 = 0x2000 RTNLGRP_DECnet_ROUTE = 0x4000 RTNLGRP_DECnet_RULE = 0x8000 RTNLGRP_NOP4 = 0x10000 RTNLGRP_IPV6_PREFIX = 0x20000 RTNLGRP_IPV6_RULE = 0x40000 # Types of messages # RTM_BASE = 16 RTM_NEWLINK = 16 RTM_DELLINK = 17 RTM_GETLINK = 18 RTM_SETLINK = 19 RTM_NEWADDR = 20 RTM_DELADDR = 21 RTM_GETADDR = 22 RTM_NEWROUTE = 24 RTM_DELROUTE = 25 RTM_GETROUTE = 26 RTM_NEWNEIGH = 28 RTM_DELNEIGH = 29 RTM_GETNEIGH = 30 RTM_NEWRULE = 32 RTM_DELRULE = 33 RTM_GETRULE = 34 RTM_NEWQDISC = 36 RTM_DELQDISC = 37 RTM_GETQDISC = 38 RTM_NEWTCLASS = 40 RTM_DELTCLASS = 41 RTM_GETTCLASS = 42 RTM_NEWTFILTER = 44 RTM_DELTFILTER = 45 RTM_GETTFILTER = 46 RTM_NEWACTION = 48 RTM_DELACTION = 49 RTM_GETACTION = 50 RTM_NEWPREFIX = 52 RTM_GETMULTICAST = 58 RTM_GETANYCAST = 62 RTM_NEWNEIGHTBL = 64 RTM_GETNEIGHTBL = 66 RTM_SETNEIGHTBL = 67 # custom message types RTM_GETBRIDGE = 88 RTM_SETBRIDGE = 89 RTM_GETBOND = 90 RTM_SETBOND = 91 RTM_GETDHCP = 92 RTM_SETDHCP = 93 (RTM_NAMES, RTM_VALUES) = map_namespace('RTM', globals()) TC_H_INGRESS = 0xfffffff1 TC_H_ROOT = 0xffffffff RTNL_GROUPS = RTNLGRP_IPV4_IFADDR |\ RTNLGRP_IPV6_IFADDR |\ RTNLGRP_IPV4_ROUTE |\ RTNLGRP_IPV6_ROUTE |\ RTNLGRP_NEIGH |\ RTNLGRP_LINK |\ RTNLGRP_TC rtypes = {'RTN_UNSPEC': 0, 'RTN_UNICAST': 1, # Gateway or direct route 'RTN_LOCAL': 2, # Accept locally 'RTN_BROADCAST': 3, # Accept locally as broadcast # send as broadcast 'RTN_ANYCAST': 4, # Accept locally as broadcast, # but send as unicast 'RTN_MULTICAST': 5, # Multicast route 'RTN_BLACKHOLE': 6, # Drop 'RTN_UNREACHABLE': 7, # Destination is unreachable 'RTN_PROHIBIT': 8, # Administratively prohibited 'RTN_THROW': 9, # Not in this table 'RTN_NAT': 10, # Translate this address 'RTN_XRESOLVE': 11} # Use external resolver rtprotos = {'RTPROT_UNSPEC': 0, 'RTPROT_REDIRECT': 1, # Route installed by ICMP redirects; # not used by current IPv4 'RTPROT_KERNEL': 2, # Route installed by kernel 'RTPROT_BOOT': 3, # Route installed during boot 'RTPROT_STATIC': 4, # Route installed by administrator # Values of protocol >= RTPROT_STATIC are not # interpreted by kernel; # keep in sync with iproute2 ! 'RTPROT_GATED': 8, # gated 'RTPROT_RA': 9, # RDISC/ND router advertisements 'RTPROT_MRT': 10, # Merit MRT 'RTPROT_ZEBRA': 11, # Zebra 'RTPROT_BIRD': 12, # BIRD 'RTPROT_DNROUTED': 13, # DECnet routing daemon 'RTPROT_XORP': 14, # XORP 'RTPROT_NTK': 15, # Netsukuku 'RTPROT_DHCP': 16} # DHCP client rtscopes = {'RT_SCOPE_UNIVERSE': 0, 'RT_SCOPE_SITE': 200, 'RT_SCOPE_LINK': 253, 'RT_SCOPE_HOST': 254, 'RT_SCOPE_NOWHERE': 255} class MarshalRtnl(Marshal): msg_map = {RTM_NEWLINK: ifinfmsg, RTM_DELLINK: ifinfmsg, RTM_GETLINK: ifinfmsg, RTM_SETLINK: ifinfmsg, RTM_NEWADDR: ifaddrmsg, RTM_DELADDR: ifaddrmsg, RTM_GETADDR: ifaddrmsg, RTM_NEWROUTE: rtmsg, RTM_DELROUTE: rtmsg, RTM_GETROUTE: rtmsg, RTM_NEWRULE: rtmsg, RTM_DELRULE: rtmsg, RTM_GETRULE: rtmsg, RTM_NEWNEIGH: ndmsg, RTM_DELNEIGH: ndmsg, RTM_GETNEIGH: ndmsg, RTM_NEWQDISC: tcmsg, RTM_DELQDISC: tcmsg, RTM_GETQDISC: tcmsg, RTM_NEWTCLASS: tcmsg, RTM_DELTCLASS: tcmsg, RTM_GETTCLASS: tcmsg, RTM_NEWTFILTER: tcmsg, RTM_DELTFILTER: tcmsg, RTM_GETTFILTER: tcmsg, RTM_GETDHCP: dhcpmsg, RTM_SETDHCP: dhcpmsg} def fix_message(self, msg): # FIXME: pls do something with it try: msg['event'] = RTM_VALUES[msg['header']['type']] except: pass class IPRSocketMixin(object): def __init__(self): super(IPRSocketMixin, self).__init__(NETLINK_ROUTE) self.marshal = MarshalRtnl() self.ancient = ANCIENT self._s_channel = None self._sproxy = NetlinkProxy(policy='return', nl=self) self._sproxy.pmap = {RTM_NEWLINK: proxy_newlink, RTM_SETLINK: proxy_setlink, RTM_DELLINK: proxy_dellink} self._rproxy = NetlinkProxy(policy='forward', nl=self) self._rproxy.pmap = {RTM_NEWLINK: proxy_linkinfo} self._sendto = self.sendto self._recv = self.recv self.sendto = self.proxy_sendto self.recv = self.proxy_recv def bind(self, groups=RTNL_GROUPS, async=False): super(IPRSocketMixin, self).bind(groups, async=async) ## # proxy-ng protocol # def proxy_sendto(self, data, address): ret = self._sproxy.handle(data) if ret is not None: if ret['verdict'] == 'forward': return self._sendto(ret['data'], address) elif ret['verdict'] in ('return', 'error'): if self._s_channel is not None: return self._s_channel.send(ret['data']) else: msgs = self.marshal.parse(ret['data']) for msg in msgs: seq = msg['header']['sequence_number'] if seq in self.backlog: self.backlog[seq].append(msg) else: self.backlog[seq] = [msg] return len(ret['data']) else: ValueError('Incorrect verdict') return self._sendto(data, address) def proxy_recv(self, bufsize, flags=0): data = self._recv(bufsize, flags) ret = self._rproxy.handle(data) if ret is not None: if ret['verdict'] in ('forward', 'error'): return ret['data'] else: ValueError('Incorrect verdict') return data class IPRSocket(IPRSocketMixin, NetlinkSocket): ''' The simplest class, that connects together the netlink parser and a generic Python socket implementation. Provides method get() to receive the next message from netlink socket and parse it. It is just simple socket-like class, it implements no buffering or like that. It spawns no additional threads, leaving this up to developers. Please note, that netlink is an asynchronous protocol with non-guaranteed delivery. You should be fast enough to get all the messages in time. If the message flow rate is higher than the speed you parse them with, exceeding messages will be dropped. *Usage* Threadless RT netlink monitoring with blocking I/O calls: >>> from pyroute2 import IPRSocket >>> from pprint import pprint >>> s = IPRSocket() >>> s.bind() >>> pprint(s.get()) [{'attrs': [('RTA_TABLE', 254), ('RTA_DST', '2a00:1450:4009:808::1002'), ('RTA_GATEWAY', 'fe80:52:0:2282::1fe'), ('RTA_OIF', 2), ('RTA_PRIORITY', 0), ('RTA_CACHEINFO', {'rta_clntref': 0, 'rta_error': 0, 'rta_expires': 0, 'rta_id': 0, 'rta_lastuse': 5926, 'rta_ts': 0, 'rta_tsage': 0, 'rta_used': 1})], 'dst_len': 128, 'event': 'RTM_DELROUTE', 'family': 10, 'flags': 512, 'header': {'error': None, 'flags': 0, 'length': 128, 'pid': 0, 'sequence_number': 0, 'type': 25}, 'proto': 9, 'scope': 0, 'src_len': 0, 'table': 254, 'tos': 0, 'type': 1}] >>> ''' pass pyroute2-0.3.5/pyroute2/netlink/rtnl/rtmsg.py0000664000175000017500000000532412453232161021153 0ustar peetpeet00000000000000 from pyroute2.netlink import nlmsg from pyroute2.netlink import nla class rtmsg(nlmsg): ''' Routing update message struct rtmsg { unsigned char rtm_family; /* Address family of route */ unsigned char rtm_dst_len; /* Length of destination */ unsigned char rtm_src_len; /* Length of source */ unsigned char rtm_tos; /* TOS filter */ unsigned char rtm_table; /* Routing table ID */ unsigned char rtm_protocol; /* Routing protocol; see below */ unsigned char rtm_scope; /* See below */ unsigned char rtm_type; /* See below */ unsigned int rtm_flags; }; ''' prefix = 'RTA_' fields = (('family', 'B'), ('dst_len', 'B'), ('src_len', 'B'), ('tos', 'B'), ('table', 'B'), ('proto', 'B'), ('scope', 'B'), ('type', 'B'), ('flags', 'I')) nla_map = (('RTA_UNSPEC', 'none'), ('RTA_DST', 'ipaddr'), ('RTA_SRC', 'ipaddr'), ('RTA_IIF', 'uint32'), ('RTA_OIF', 'uint32'), ('RTA_GATEWAY', 'ipaddr'), ('RTA_PRIORITY', 'uint32'), ('RTA_PREFSRC', 'ipaddr'), ('RTA_METRICS', 'metrics'), ('RTA_MULTIPATH', 'hex'), ('RTA_PROTOINFO', 'uint32'), ('RTA_FLOW', 'hex'), ('RTA_CACHEINFO', 'cacheinfo'), ('RTA_SESSION', 'hex'), ('RTA_MP_ALGO', 'hex'), ('RTA_TABLE', 'uint32'), ('RTA_MARK', 'uint32')) class metrics(nla): prefix = 'RTAX_' nla_map = (('RTAX_UNSPEC', 'none'), ('RTAX_LOCK', 'uint32'), ('RTAX_MTU', 'uint32'), ('RTAX_WINDOW', 'uint32'), ('RTAX_RTT', 'uint32'), ('RTAX_RTTVAR', 'uint32'), ('RTAX_SSTHRESH', 'uint32'), ('RTAX_CWND', 'uint32'), ('RTAX_ADVMSS', 'uint32'), ('RTAX_REORDERING', 'uint32'), ('RTAX_HOPLIMIT', 'uint32'), ('RTAX_INITCWND', 'uint32'), ('RTAX_FEATURES', 'uint32'), ('RTAX_RTO_MIN', 'uint32'), ('RTAX_INITRWND', 'uint32'), ('RTAX_QUICKACK', 'uint32')) class cacheinfo(nla): fields = (('rta_clntref', 'I'), ('rta_lastuse', 'I'), ('rta_expires', 'i'), ('rta_error', 'I'), ('rta_used', 'I'), ('rta_id', 'I'), ('rta_ts', 'I'), ('rta_tsage', 'I')) pyroute2-0.3.5/pyroute2/netlink/rtnl/ndmsg.py0000664000175000017500000000270612441340231021123 0ustar peetpeet00000000000000 from pyroute2.netlink import nlmsg from pyroute2.netlink import nla class ndmsg(nlmsg): ''' ARP cache update message struct ndmsg { unsigned char ndm_family; int ndm_ifindex; /* Interface index */ __u16 ndm_state; /* State */ __u8 ndm_flags; /* Flags */ __u8 ndm_type; }; struct nda_cacheinfo { __u32 ndm_confirmed; __u32 ndm_used; __u32 ndm_updated; __u32 ndm_refcnt; }; ''' fields = (('family', 'i'), ('ifindex', 'i'), ('state', 'H'), ('flags', 'B'), ('ndm_type', 'B')) # Please note, that nla_map creates implicit # enumeration. In this case it will be: # # NDA_UNSPEC = 0 # NDA_DST = 1 # NDA_LLADDR = 2 # NDA_CACHEINFO = 3 # NDA_PROBES = 4 # ... # nla_map = (('NDA_UNSPEC', 'none'), ('NDA_DST', 'ipaddr'), ('NDA_LLADDR', 'l2addr'), ('NDA_CACHEINFO', 'cacheinfo'), ('NDA_PROBES', 'uint32'), ('NDA_VLAN', 'uint16'), ('NDA_PORT', 'be16'), ('NDA_VNI', 'be32'), ('NDA_IFINDEX', 'uint32')) class cacheinfo(nla): fields = (('ndm_confirmed', 'I'), ('ndm_used', 'I'), ('ndm_updated', 'I'), ('ndm_refcnt', 'I')) pyroute2-0.3.5/pyroute2/netlink/rtnl/ifaddrmsg.py0000664000175000017500000000522512446114341021760 0ustar peetpeet00000000000000import socket from pyroute2.common import map_namespace from pyroute2.netlink import nlmsg from pyroute2.netlink import nla # address attributes # # Important comment: # For IPv4, IFA_ADDRESS is a prefix address, not a local interface # address. It makes no difference for normal interfaces, but # for point-to-point ones IFA_ADDRESS means DESTINATION address, # and the local address is supplied in IFA_LOCAL attribute. # IFA_F_SECONDARY = 0x01 # IFA_F_TEMPORARY IFA_F_SECONDARY IFA_F_NODAD = 0x02 IFA_F_OPTIMISTIC = 0x04 IFA_F_DADFAILED = 0x08 IFA_F_HOMEADDRESS = 0x10 IFA_F_DEPRECATED = 0x20 IFA_F_TENTATIVE = 0x40 IFA_F_PERMANENT = 0x80 IFA_F_MANAGETEMPADDR = 0x100 IFA_F_NOPREFIXROUTE = 0x200 (IFA_F_NAMES, IFA_F_VALUES) = map_namespace('IFA_F', globals()) # 8<---------------------------------------------- IFA_F_TEMPORARY = IFA_F_SECONDARY IFA_F_NAMES['IFA_F_TEMPORARY'] = IFA_F_TEMPORARY IFA_F_VALUES6 = IFA_F_VALUES IFA_F_VALUES6[IFA_F_TEMPORARY] = 'IFA_F_TEMPORARY' # 8<---------------------------------------------- class ifaddrmsg(nlmsg): ''' IP address information struct ifaddrmsg { unsigned char ifa_family; /* Address type */ unsigned char ifa_prefixlen; /* Prefixlength of address */ unsigned char ifa_flags; /* Address flags */ unsigned char ifa_scope; /* Address scope */ int ifa_index; /* Interface index */ }; ''' prefix = 'IFA_' fields = (('family', 'B'), ('prefixlen', 'B'), ('flags', 'B'), ('scope', 'B'), ('index', 'I')) nla_map = (('IFA_UNSPEC', 'hex'), ('IFA_ADDRESS', 'ipaddr'), ('IFA_LOCAL', 'ipaddr'), ('IFA_LABEL', 'asciiz'), ('IFA_BROADCAST', 'ipaddr'), ('IFA_ANYCAST', 'ipaddr'), ('IFA_CACHEINFO', 'cacheinfo'), ('IFA_MULTICAST', 'ipaddr'), ('IFA_FLAGS', 'uint32')) class cacheinfo(nla): fields = (('ifa_prefered', 'I'), ('ifa_valid', 'I'), ('cstamp', 'I'), ('tstamp', 'I')) @staticmethod def flags2names(flags, family=socket.AF_INET): if family == socket.AF_INET6: ifa_f_values = IFA_F_VALUES6 else: ifa_f_values = IFA_F_VALUES ret = [] for f in ifa_f_values: if f & flags: ret.append(ifa_f_values[f]) return ret @staticmethod def names2flags(flags): ret = 0 for f in flags: if f[0] == '!': f = f[1:] else: ret |= IFA_F_NAMES[f] return ret pyroute2-0.3.5/pyroute2/netlink/rtnl/iw_event.py0000664000175000017500000000737512432725447021662 0ustar peetpeet00000000000000from pyroute2.netlink import nla class iw_event(nla): nla_map = ((0x8B00, 'SIOCSIWCOMMIT', 'hex'), (0x8B01, 'SIOCGIWNAME', 'hex'), # Basic operations (0x8B02, 'SIOCSIWNWID', 'hex'), (0x8B03, 'SIOCGIWNWID', 'hex'), (0x8B04, 'SIOCSIWFREQ', 'hex'), (0x8B05, 'SIOCGIWFREQ', 'hex'), (0x8B06, 'SIOCSIWMODE', 'hex'), (0x8B07, 'SIOCGIWMODE', 'hex'), (0x8B08, 'SIOCSIWSENS', 'hex'), (0x8B09, 'SIOCGIWSENS', 'hex'), # Informative stuff (0x8B0A, 'SIOCSIWRANGE', 'hex'), (0x8B0B, 'SIOCGIWRANGE', 'hex'), (0x8B0C, 'SIOCSIWPRIV', 'hex'), (0x8B0D, 'SIOCGIWPRIV', 'hex'), (0x8B0E, 'SIOCSIWSTATS', 'hex'), (0x8B0F, 'SIOCGIWSTATS', 'hex'), # Spy support (statistics per MAC address - # used for Mobile IP support) (0x8B10, 'SIOCSIWSPY', 'hex'), (0x8B11, 'SIOCGIWSPY', 'hex'), (0x8B12, 'SIOCSIWTHRSPY', 'hex'), (0x8B13, 'SIOCGIWTHRSPY', 'hex'), # Access Point manipulation (0x8B14, 'SIOCSIWAP', 'hex'), (0x8B15, 'SIOCGIWAP', 'hex'), (0x8B17, 'SIOCGIWAPLIST', 'hex'), (0x8B18, 'SIOCSIWSCAN', 'hex'), (0x8B19, 'SIOCGIWSCAN', 'hex'), # 802.11 specific support (0x8B1A, 'SIOCSIWESSID', 'hex'), (0x8B1B, 'SIOCGIWESSID', 'hex'), (0x8B1C, 'SIOCSIWNICKN', 'hex'), (0x8B1D, 'SIOCGIWNICKN', 'hex'), # Other parameters useful in 802.11 and # some other devices (0x8B20, 'SIOCSIWRATE', 'hex'), (0x8B21, 'SIOCGIWRATE', 'hex'), (0x8B22, 'SIOCSIWRTS', 'hex'), (0x8B23, 'SIOCGIWRTS', 'hex'), (0x8B24, 'SIOCSIWFRAG', 'hex'), (0x8B25, 'SIOCGIWFRAG', 'hex'), (0x8B26, 'SIOCSIWTXPOW', 'hex'), (0x8B27, 'SIOCGIWTXPOW', 'hex'), (0x8B28, 'SIOCSIWRETRY', 'hex'), (0x8B29, 'SIOCGIWRETRY', 'hex'), # Encoding stuff (scrambling, hardware security, WEP...) (0x8B2A, 'SIOCSIWENCODE', 'hex'), (0x8B2B, 'SIOCGIWENCODE', 'hex'), # Power saving stuff (power management, unicast # and multicast) (0x8B2C, 'SIOCSIWPOWER', 'hex'), (0x8B2D, 'SIOCGIWPOWER', 'hex'), # WPA : Generic IEEE 802.11 informatiom element # (e.g., for WPA/RSN/WMM). (0x8B30, 'SIOCSIWGENIE', 'hex'), (0x8B31, 'SIOCGIWGENIE', 'hex'), # WPA : IEEE 802.11 MLME requests (0x8B16, 'SIOCSIWMLME', 'hex'), # WPA : Authentication mode parameters (0x8B32, 'SIOCSIWAUTH', 'hex'), (0x8B33, 'SIOCGIWAUTH', 'hex'), # WPA : Extended version of encoding configuration (0x8B34, 'SIOCSIWENCODEEXT', 'hex'), (0x8B35, 'SIOCGIWENCODEEXT', 'hex'), # WPA2 : PMKSA cache management (0x8B36, 'SIOCSIWPMKSA', 'hex'), # Events s.str. (0x8C00, 'IWEVTXDROP', 'hex'), (0x8C01, 'IWEVQUAL', 'hex'), (0x8C02, 'IWEVCUSTOM', 'hex'), (0x8C03, 'IWEVREGISTERED', 'hex'), (0x8C04, 'IWEVEXPIRED', 'hex'), (0x8C05, 'IWEVGENIE', 'hex'), (0x8C06, 'IWEVMICHAELMICFAILURE', 'hex'), (0x8C07, 'IWEVASSOCREQIE', 'hex'), (0x8C08, 'IWEVASSOCRESPIE', 'hex'), (0x8C09, 'IWEVPMKIDCAND', 'hex')) pyroute2-0.3.5/pyroute2/netlink/rtnl/errmsg.py0000664000175000017500000000023312432725447021323 0ustar peetpeet00000000000000from pyroute2.netlink import nlmsg class errmsg(nlmsg): ''' Custom message type Error ersatz-message ''' fields = (('code', 'i'), ) pyroute2-0.3.5/pyroute2/netlink/rtnl/tcmsg.py0000664000175000017500000007466612432725447021166 0ustar peetpeet00000000000000import re import os import struct from pyroute2.common import size_suffixes from pyroute2.common import time_suffixes from pyroute2.common import rate_suffixes from pyroute2.common import basestring from pyroute2.netlink import nlmsg from pyroute2.netlink import nla TCA_ACT_MAX_PRIO = 32 LINKLAYER_UNSPEC = 0 LINKLAYER_ETHERNET = 1 LINKLAYER_ATM = 2 ATM_CELL_SIZE = 53 ATM_CELL_PAYLOAD = 48 TC_RED_ECN = 1 TC_RED_HARDDROP = 2 TC_RED_ADAPTATIVE = 4 TIME_UNITS_PER_SEC = 1000000 _psched = open('/proc/net/psched', 'r') [_t2us, _us2t, _clock_res, _wee] = [int(i, 16) for i in _psched.read().split()] _clock_factor = float(_clock_res) / TIME_UNITS_PER_SEC _tick_in_usec = float(_t2us) / _us2t * _clock_factor _first_letter = re.compile('[^0-9]+') _psched.close() def _get_hz(): if _clock_res == 1000000: return _wee else: return os.environ.get('HZ', 1000) def _get_by_suffix(value, default, func): if not isinstance(value, basestring): return value pos = _first_letter.search(value) if pos is None: suffix = default else: pos = pos.start() value, suffix = value[:pos], value[pos:] value = int(value) return func(value, suffix) def _get_size(size): return _get_by_suffix(size, 'b', lambda x, y: x * size_suffixes[y]) def _get_time(lat): return _get_by_suffix(lat, 'ms', lambda x, y: (x * TIME_UNITS_PER_SEC) / time_suffixes[y]) def _get_rate(rate): return _get_by_suffix(rate, 'bit', lambda x, y: (x * rate_suffixes[y]) / 8) def _time2tick(time): # The current code is ported from tc utility return int(time) * _tick_in_usec def _calc_xmittime(rate, size): # The current code is ported from tc utility return int(_time2tick(TIME_UNITS_PER_SEC * (float(size) / rate))) def _percent2u32(pct): '''xlate a percentage to an uint32 value 0% -> 0 100% -> 2**32 - 1''' return int((2**32 - 1)*pct/100) def _red_eval_ewma(qmin, burst, avpkt): # The current code is ported from tc utility wlog = 1 W = 0.5 a = float(burst) + 1 - float(qmin) / avpkt assert a < 1 while wlog < 32: wlog += 1 W /= 2 if (a <= (1 - pow(1 - W, burst)) / W): return wlog return -1 def _red_eval_P(qmin, qmax, probability): # The current code is ported from tc utility i = qmax - qmin assert i > 0 assert i < 32 probability /= i while i < 32: i += 1 if probability > 1: break probability *= 2 return i def _get_rate_parameters(kwarg): # rate and burst are required rate = _get_rate(kwarg['rate']) burst = kwarg['burst'] # if peak, mtu is required peak = _get_rate(kwarg.get('peak', 0)) mtu = kwarg.get('mtu', 0) if peak: assert mtu # limit OR latency is required limit = kwarg.get('limit', None) latency = _get_time(kwarg.get('latency', None)) assert limit is not None or latency is not None # calculate limit from latency if limit is None: rate_limit = rate * float(latency) /\ TIME_UNITS_PER_SEC + burst if peak: peak_limit = peak * float(latency) /\ TIME_UNITS_PER_SEC + mtu if rate_limit > peak_limit: rate_limit = peak_limit limit = rate_limit return {'rate': int(rate), 'mtu': mtu, 'buffer': _calc_xmittime(rate, burst), 'limit': int(limit)} def get_tbf_parameters(kwarg): parms = _get_rate_parameters(kwarg) # fill parameters return {'attrs': [['TCA_TBF_PARMS', parms], ['TCA_TBF_RTAB', True]]} def _get_filter_police_parameter(kwarg): # if no limit specified, set it to zero to make # the next call happy kwarg['limit'] = kwarg.get('limit', 0) tbfp = _get_rate_parameters(kwarg) # create an alias -- while TBF uses 'buffer', rate # policy uses 'burst' tbfp['burst'] = tbfp['buffer'] # action resolver actions = nla_plus_police.police.police_tbf.actions tbfp['action'] = actions[kwarg.get('action', 'reclassify')] police = [['TCA_POLICE_TBF', tbfp], ['TCA_POLICE_RATE', True]] return police def get_u32_parameters(kwarg): ret = {'attrs': []} if kwarg.get('rate'): ret['attrs'].append([ 'TCA_U32_POLICE', {'attrs': _get_filter_police_parameter(kwarg)} ]) ret['attrs'].append(['TCA_U32_CLASSID', kwarg['target']]) ret['attrs'].append(['TCA_U32_SEL', {'keys': kwarg['keys']}]) return ret def get_fw_parameters(kwarg): ret = {'attrs': []} attrs_map = ( ('classid', 'TCA_FW_CLASSID'), ('act', 'TCA_FW_ACT'), # ('police', 'TCA_FW_POLICE'), # Handled in _get_filter_police_parameter ('indev', 'TCA_FW_INDEV'), ('mask', 'TCA_FW_MASK'), ) if kwarg.get('rate'): ret['attrs'].append([ 'TCA_FW_POLICE', {'attrs': _get_filter_police_parameter(kwarg)} ]) for k, v in attrs_map: r = kwarg.get(k, None) if r is not None: ret['attrs'].append([v, r]) return ret def get_sfq_parameters(kwarg): kwarg['quantum'] = _get_size(kwarg.get('quantum', 0)) kwarg['perturb_period'] = kwarg.get('perturb', 0) or \ kwarg.get('perturb_period', 0) limit = kwarg['limit'] = kwarg.get('limit', 0) or \ kwarg.get('redflowlimit', 0) qth_min = kwarg.get('min', 0) qth_max = kwarg.get('max', 0) avpkt = kwarg.get('avpkt', 1000) probability = kwarg.get('probability', 0.02) ecn = kwarg.get('ecn', False) harddrop = kwarg.get('harddrop', False) kwarg['flags'] = kwarg.get('flags', 0) if ecn: kwarg['flags'] |= TC_RED_ECN if harddrop: kwarg['flags'] |= TC_RED_HARDDROP if kwarg.get('redflowlimit'): qth_max = qth_max or limit / 4 qth_min = qth_min or qth_max / 3 kwarg['burst'] = kwarg['burst'] or \ (2 * qth_min + qth_max) / (3 * avpkt) assert limit > qth_max assert qth_max > qth_min kwarg['qth_min'] = qth_min kwarg['qth_max'] = qth_max kwarg['Wlog'] = _red_eval_ewma(qth_min, kwarg['burst'], avpkt) kwarg['Plog'] = _red_eval_P(qth_min, qth_max, probability) assert kwarg['Wlog'] >= 0 assert kwarg['Plog'] >= 0 kwarg['max_P'] = int(probability * pow(2, 23)) return kwarg def get_htb_class_parameters(kwarg): # prio = kwarg.get('prio', 0) mtu = kwarg.get('mtu', 1600) mpu = kwarg.get('mpu', 0) overhead = kwarg.get('overhead', 0) quantum = kwarg.get('quantum', 0) # rate = _get_rate(kwarg.get('rate', None)) ceil = _get_rate(kwarg.get('ceil', 0)) or rate burst = kwarg.get('burst', None) or \ kwarg.get('maxburst', None) or \ kwarg.get('buffer', None) if rate is not None: if burst is None: burst = rate / _get_hz() + mtu burst = _calc_xmittime(rate, burst) cburst = kwarg.get('cburst', None) or \ kwarg.get('cmaxburst', None) or \ kwarg.get('cbuffer', None) if ceil is not None: if cburst is None: cburst = ceil / _get_hz() + mtu cburst = _calc_xmittime(ceil, cburst) return {'attrs': [['TCA_HTB_PARMS', {'buffer': burst, 'cbuffer': cburst, 'quantum': quantum, 'prio': prio, 'rate': rate, 'ceil': ceil, 'ceil_overhead': overhead, 'rate_overhead': overhead, 'rate_mpu': mpu, 'ceil_mpu': mpu}], ['TCA_HTB_RTAB', True], ['TCA_HTB_CTAB', True]]} def get_htb_parameters(kwarg): rate2quantum = kwarg.get('r2q', 0xa) version = kwarg.get('version', 3) defcls = kwarg.get('default', 0x10) return {'attrs': [['TCA_HTB_INIT', {'debug': 0, 'defcls': defcls, 'direct_pkts': 0, 'rate2quantum': rate2quantum, 'version': version}]]} def get_netem_parameters(kwarg): delay = _time2tick(kwarg.get('delay', 0)) # in microsecond limit = kwarg.get('limit', 1000) # fifo limit (packets) see netem.c:230 loss = _percent2u32(kwarg.get('loss', 0)) # int percentage gap = kwarg.get('gap', 0) duplicate = kwarg.get('duplicate', 0) jitter = _time2tick(kwarg.get('jitter', 0)) # in microsecond opts = { 'delay': delay, 'limit': limit, 'loss': loss, 'gap': gap, 'duplicate': duplicate, 'jitter': jitter, 'attrs': [] } # correlation (delay, loss, duplicate) delay_corr = _percent2u32(kwarg.get('delay_corr', 0)) loss_corr = _percent2u32(kwarg.get('loss_corr', 0)) dup_corr = _percent2u32(kwarg.get('dup_corr', 0)) if delay_corr or loss_corr or dup_corr: # delay_corr requires that both jitter and delay are != 0 if delay_corr and not (delay and jitter): raise Exception('delay correlation requires delay' ' and jitter to be set') # loss correlation and loss if loss_corr and not loss: raise Exception('loss correlation requires loss to be set') # duplicate correlation and duplicate if dup_corr and not duplicate: raise Exception('duplicate correlation requires ' 'duplicate to be set') opts['attrs'].append(['TCA_NETEM_CORR', {'delay_corr': delay_corr, 'loss_corr': loss_corr, 'dup_corr': dup_corr}]) # reorder (probability, correlation) prob_reorder = _percent2u32(kwarg.get('prob_reorder', 0)) corr_reorder = _percent2u32(kwarg.get('corr_reorder', 0)) if prob_reorder != 0: # gap defaults to 1 if equal to 0 if gap == 0: opts['gap'] = gap = 1 opts['attrs'].append(['TCA_NETEM_REORDER', {'prob_reorder': prob_reorder, 'corr_reorder': corr_reorder}]) else: if gap != 0: raise Exception('gap can only be set when prob_reorder is set') elif corr_reorder != 0: raise Exception('corr_reorder can only be set when ' 'prob_reorder is set') # corrupt (probability, correlation) prob_corrupt = _percent2u32(kwarg.get('prob_corrupt', 0)) corr_corrupt = _percent2u32(kwarg.get('corr_corrupt', 0)) if prob_corrupt: opts['attrs'].append(['TCA_NETEM_CORRUPT', {'prob_corrupt': prob_corrupt, 'corr_corrupt': corr_corrupt}]) elif corr_corrupt != 0: raise Exception('corr_corrupt can only be set when ' 'prob_corrupt is set') # TODO # delay distribution (dist_size, dist_data) return opts class nla_plus_rtab(nla): class parms(nla): def adjust_size(self, size, mpu, linklayer): # The current code is ported from tc utility if size < mpu: size = mpu if linklayer == LINKLAYER_ATM: cells = size / ATM_CELL_PAYLOAD if size % ATM_CELL_PAYLOAD > 0: cells += 1 size = cells * ATM_CELL_SIZE return size def calc_rtab(self, kind): # The current code is ported from tc utility rtab = [] mtu = self.get('mtu', 0) or 1600 cell_log = self['%s_cell_log' % (kind)] mpu = self['%s_mpu' % (kind)] rate = self.get(kind, 'rate') # calculate cell_log if cell_log == 0: while (mtu >> cell_log) > 255: cell_log += 1 # fill up the table for i in range(256): size = self.adjust_size((i + 1) << cell_log, mpu, LINKLAYER_ETHERNET) rtab.append(_calc_xmittime(rate, size)) self['%s_cell_align' % (kind)] = -1 self['%s_cell_log' % (kind)] = cell_log return rtab def encode(self): self.rtab = None self.ptab = None if self.get('rate', False): self.rtab = self.calc_rtab('rate') if self.get('peak', False): self.ptab = self.calc_rtab('peak') if self.get('ceil', False): self.ctab = self.calc_rtab('ceil') nla.encode(self) class rtab(nla): fields = (('value', 's'), ) def encode(self): parms = self.parent.get_encoded('TCA_TBF_PARMS') or \ self.parent.get_encoded('TCA_HTB_PARMS') or \ self.parent.get_encoded('TCA_POLICE_TBF') if parms is not None: self.value = getattr(parms, self.__class__.__name__) self['value'] = struct.pack('I' * 256, *(int(x) for x in self.value)) nla.encode(self) def decode(self): nla.decode(self) parms = self.parent.get_attr('TCA_TBF_PARMS') or \ self.parent.get_attr('TCA_HTB_PARMS') or \ self.parent.get_attr('TCA_POLICE_TBF') if parms is not None: rtab = struct.unpack('I' * (len(self['value']) / 4), self['value']) self.value = rtab setattr(parms, self.__class__.__name__, rtab) class ptab(rtab): pass class ctab(rtab): pass class nla_plus_stats2(object): class stats2(nla): nla_map = (('TCA_STATS_UNSPEC', 'none'), ('TCA_STATS_BASIC', 'basic'), ('TCA_STATS_RATE_EST', 'rate_est'), ('TCA_STATS_QUEUE', 'queue'), ('TCA_STATS_APP', 'hex')) class basic(nla): fields = (('bytes', 'Q'), ('packets', 'Q')) class rate_est(nla): fields = (('bps', 'I'), ('pps', 'I')) class queue(nla): fields = (('qlen', 'I'), ('backlog', 'I'), ('drops', 'I'), ('requeues', 'I'), ('overlimits', 'I')) class stats2_hfsc(stats2): nla_map = (('TCA_STATS_UNSPEC', 'none'), ('TCA_STATS_BASIC', 'basic'), ('TCA_STATS_RATE_EST', 'rate_est'), ('TCA_STATS_QUEUE', 'queue'), ('TCA_STATS_APP', 'stats_app_hfsc')) class stats_app_hfsc(nla): fields = (('work', 'Q'), # total work done ('rtwork', 'Q'), # total work done by real-time criteria ('period', 'I'), # current period ('level', 'I')) # class level in hierarchy class nla_plus_police(object): class police(nla_plus_rtab): nla_map = (('TCA_POLICE_UNSPEC', 'none'), ('TCA_POLICE_TBF', 'police_tbf'), ('TCA_POLICE_RATE', 'rtab'), ('TCA_POLICE_PEAKRATE', 'ptab'), ('TCA_POLICE_AVRATE', 'uint32'), ('TCA_POLICE_RESULT', 'uint32')) class police_tbf(nla_plus_rtab.parms): fields = (('index', 'I'), ('action', 'i'), ('limit', 'I'), ('burst', 'I'), ('mtu', 'I'), ('rate_cell_log', 'B'), ('rate___reserved', 'B'), ('rate_overhead', 'H'), ('rate_cell_align', 'h'), ('rate_mpu', 'H'), ('rate', 'I'), ('peak_cell_log', 'B'), ('peak___reserved', 'B'), ('peak_overhead', 'H'), ('peak_cell_align', 'h'), ('peak_mpu', 'H'), ('peak', 'I'), ('refcnt', 'i'), ('bindcnt', 'i'), ('capab', 'I')) actions = {'unspec': -1, # TC_POLICE_UNSPEC 'ok': 0, # TC_POLICE_OK 'reclassify': 1, # TC_POLICE_RECLASSIFY 'shot': 2, # TC_POLICE_SHOT 'drop': 2, # TC_POLICE_SHOT 'pipe': 3} # TC_POLICE_PIPE class tcmsg(nlmsg, nla_plus_stats2): prefix = 'TCA_' fields = (('family', 'B'), ('pad1', 'B'), ('pad2', 'H'), ('index', 'i'), ('handle', 'I'), ('parent', 'I'), ('info', 'I')) nla_map = (('TCA_UNSPEC', 'none'), ('TCA_KIND', 'asciiz'), ('TCA_OPTIONS', 'get_options'), ('TCA_STATS', 'stats'), ('TCA_XSTATS', 'get_xstats'), ('TCA_RATE', 'hex'), ('TCA_FCNT', 'hex'), ('TCA_STATS2', 'get_stats2'), ('TCA_STAB', 'hex')) class stats(nla): fields = (('bytes', 'Q'), ('packets', 'I'), ('drop', 'I'), ('overlimits', 'I'), ('bps', 'I'), ('pps', 'I'), ('qlen', 'I'), ('backlog', 'I')) def get_stats2(self, *argv, **kwarg): kind = self.get_attr('TCA_KIND') if kind == 'hfsc': return self.stats2_hfsc return self.stats2 def get_xstats(self, *argv, **kwarg): kind = self.get_attr('TCA_KIND') if kind == 'htb': return self.xstats_htb return self.hex class xstats_htb(nla): fields = (('lends', 'I'), ('borrows', 'I'), ('giants', 'I'), ('tokens', 'I'), ('ctokens', 'I')) def get_options(self, *argv, **kwarg): kind = self.get_attr('TCA_KIND') if kind == 'ingress': return self.options_ingress elif kind == 'pfifo_fast': return self.options_pfifo_fast elif kind == 'tbf': return self.options_tbf elif kind == 'sfq': if kwarg.get('length', 0) >= self.options_sfq_v1.get_size(): return self.options_sfq_v1 else: return self.options_sfq_v0 elif kind == 'hfsc': return self.options_hfsc elif kind == 'htb': return self.options_htb elif kind == 'netem': return self.options_netem elif kind == 'u32': return self.options_u32 elif kind == 'fw': return self.options_fw return self.hex class options_ingress(nla): fields = (('value', 'I'), ) class options_hfsc(nla): nla_map = (('TCA_HFSC_UNSPEC', 'hfsc_qopt'), ('TCA_HFSC_RSC', 'hfsc_curve'), # real-time curve ('TCA_HFSC_FSC', 'hfsc_curve'), # link-share curve ('TCA_HFSC_USC', 'hfsc_curve')) # upper-limit curve class hfsc_qopt(nla): fields = (('defcls', 'H'),) # default class class hfsc_curve(nla): fields = (('m1', 'I'), # slope of the first segment in bps ('d', 'I'), # x-projection of the first segment in us ('m2', 'I')) # slope of the second segment in bps class options_htb(nla_plus_rtab): nla_map = (('TCA_HTB_UNSPEC', 'none'), ('TCA_HTB_PARMS', 'htb_parms'), ('TCA_HTB_INIT', 'htb_glob'), ('TCA_HTB_CTAB', 'ctab'), ('TCA_HTB_RTAB', 'rtab')) class htb_glob(nla): fields = (('version', 'I'), ('rate2quantum', 'I'), ('defcls', 'I'), ('debug', 'I'), ('direct_pkts', 'I')) class htb_parms(nla_plus_rtab.parms): fields = (('rate_cell_log', 'B'), ('rate___reserved', 'B'), ('rate_overhead', 'H'), ('rate_cell_align', 'h'), ('rate_mpu', 'H'), ('rate', 'I'), ('ceil_cell_log', 'B'), ('ceil___reserved', 'B'), ('ceil_overhead', 'H'), ('ceil_cell_align', 'h'), ('ceil_mpu', 'H'), ('ceil', 'I'), ('buffer', 'I'), ('cbuffer', 'I'), ('quantum', 'I'), ('level', 'I'), ('prio', 'I')) class options_netem(nla): nla_map = (('TCA_NETEM_UNSPEC', 'none'), ('TCA_NETEM_CORR', 'netem_corr'), ('TCA_NETEM_DELAY_DIST', 'none'), ('TCA_NETEM_REORDER', 'netem_reorder'), ('TCA_NETEM_CORRUPT', 'netem_corrupt'), ('TCA_NETEM_LOSS', 'none'), ('TCA_NETEM_RATE', 'none')) fields = (('delay', 'I'), ('limit', 'I'), ('loss', 'I'), ('gap', 'I'), ('duplicate', 'I'), ('jitter', 'I')) class netem_corr(nla): '''correlation''' fields = (('delay_corr', 'I'), ('loss_corr', 'I'), ('dup_corr', 'I')) class netem_reorder(nla): '''reorder has probability and correlation''' fields = (('prob_reorder', 'I'), ('corr_reorder', 'I')) class netem_corrupt(nla): '''corruption has probability and correlation''' fields = (('prob_corrupt', 'I'), ('corr_corrupt', 'I')) class options_fw(nla, nla_plus_police): nla_map = (('TCA_FW_UNSPEC', 'none'), ('TCA_FW_CLASSID', 'uint32'), ('TCA_FW_POLICE', 'police'), # TODO string? ('TCA_FW_INDEV', 'hex'), # TODO string ('TCA_FW_ACT', 'hex'), # TODO ('TCA_FW_MASK', 'uint32')) class options_u32(nla, nla_plus_police): nla_map = (('TCA_U32_UNSPEC', 'none'), ('TCA_U32_CLASSID', 'uint32'), ('TCA_U32_HASH', 'uint32'), ('TCA_U32_LINK', 'hex'), ('TCA_U32_DIVISOR', 'uint32'), ('TCA_U32_SEL', 'u32_sel'), ('TCA_U32_POLICE', 'police'), ('TCA_U32_ACT', 'tca_act_prio'), ('TCA_U32_INDEV', 'hex'), ('TCA_U32_PCNT', 'u32_pcnt'), ('TCA_U32_MARK', 'u32_mark')) class tca_act_prio(nla): nla_map = tuple([('TCA_ACT_PRIO_%i' % x, 'tca_act') for x in range(TCA_ACT_MAX_PRIO)]) class tca_act(nla, nla_plus_police, nla_plus_stats2): nla_map = (('TCA_ACT_UNSPEC', 'none'), ('TCA_ACT_KIND', 'asciiz'), ('TCA_ACT_OPTIONS', 'police'), ('TCA_ACT_INDEX', 'hex'), ('TCA_ACT_STATS', 'stats2')) class u32_sel(nla): fields = (('flags', 'B'), ('offshift', 'B'), ('nkeys', 'B'), ('__align', 'B'), ('offmask', '>H'), ('off', 'H'), ('offoff', 'h'), ('hoff', 'h'), ('hmask', '>I')) class u32_key(nlmsg): header = None fields = (('key_mask', '>I'), ('key_val', '>I'), ('key_off', 'i'), ('key_offmask', 'i')) def encode(self): ''' 'keys': ['0x0006/0x00ff+8', '0x0000/0xffc0+2', '0x5/0xf+0', '0x10/0xff+33'] => 00060000/00ff0000 + 8 05000000/0f00ffc0 + 0 00100000/00ff0000 + 32 ''' def cut_field(key, separator): ''' split a field from the end of the string ''' field = '0' pos = key.find(separator) new_key = key if pos > 0: field = key[pos + 1:] new_key = key[:pos] return (new_key, field) # 'header' array to pack keys to header = [(0, 0) for i in range(256)] keys = [] # iterate keys and pack them to the 'header' for key in self['keys']: # TODO tags: filter (key, nh) = cut_field(key, '@') # FIXME: do not ignore nh (key, offset) = cut_field(key, '+') offset = int(offset, 0) # a little trick: if you provide /00ff+8, that # really means /ff+9, so we should take it into # account (key, mask) = cut_field(key, '/') if mask[:2] == '0x': mask = mask[2:] while True: if mask[:2] == '00': offset += 1 mask = mask[2:] else: break mask = '0x' + mask mask = int(mask, 0) value = int(key, 0) bits = 24 if mask == 0 and value == 0: key = self.u32_key(self.buf) key['key_off'] = offset key['key_mask'] = mask key['key_val'] = value keys.append(key) for bmask in struct.unpack('4B', struct.pack('>I', mask)): if bmask > 0: bvalue = (value & (bmask << bits)) >> bits header[offset] = (bvalue, bmask) offset += 1 bits -= 8 # recalculate keys from 'header' key = None value = 0 mask = 0 for offset in range(256): (bvalue, bmask) = header[offset] if bmask > 0 and key is None: key = self.u32_key(self.buf) key['key_off'] = offset key['key_mask'] = 0 key['key_val'] = 0 bits = 24 if key is not None and bits >= 0: key['key_mask'] |= bmask << bits key['key_val'] |= bvalue << bits bits -= 8 if (bits < 0 or offset == 255): keys.append(key) key = None assert keys self['nkeys'] = len(keys) # FIXME: do not hardcode flags :) self['flags'] = 1 start = self.buf.tell() nla.encode(self) for key in keys: key.encode() self.update_length(start) def decode(self): nla.decode(self) self['keys'] = [] nkeys = self['nkeys'] while nkeys: key = self.u32_key(self.buf) key.decode() self['keys'].append(key) nkeys -= 1 class u32_mark(nla): fields = (('val', 'I'), ('mask', 'I'), ('success', 'I')) class u32_pcnt(nla): fields = (('rcnt', 'Q'), ('rhit', 'Q'), ('kcnts', 'Q')) class options_pfifo_fast(nla): fields = (('bands', 'i'), ('priomap', '16B')) class options_tbf(nla_plus_rtab): nla_map = (('TCA_TBF_UNSPEC', 'none'), ('TCA_TBF_PARMS', 'tbf_parms'), ('TCA_TBF_RTAB', 'rtab'), ('TCA_TBF_PTAB', 'ptab')) class tbf_parms(nla_plus_rtab.parms): fields = (('rate_cell_log', 'B'), ('rate___reserved', 'B'), ('rate_overhead', 'H'), ('rate_cell_align', 'h'), ('rate_mpu', 'H'), ('rate', 'I'), ('peak_cell_log', 'B'), ('peak___reserved', 'B'), ('peak_overhead', 'H'), ('peak_cell_align', 'h'), ('peak_mpu', 'H'), ('peak', 'I'), ('limit', 'I'), ('buffer', 'I'), ('mtu', 'I')) class options_sfq_v0(nla): fields = (('quantum', 'I'), ('perturb_period', 'i'), ('limit', 'I'), ('divisor', 'I'), ('flows', 'I')) class options_sfq_v1(nla): fields = (('quantum', 'I'), ('perturb_period', 'i'), ('limit_v0', 'I'), ('divisor', 'I'), ('flows', 'I'), ('depth', 'I'), ('headdrop', 'I'), ('limit_v1', 'I'), ('qth_min', 'I'), ('qth_max', 'I'), ('Wlog', 'B'), ('Plog', 'B'), ('Scell_log', 'B'), ('flags', 'B'), ('max_P', 'I'), ('prob_drop', 'I'), ('forced_drop', 'I'), ('prob_mark', 'I'), ('forced_mark', 'I'), ('prob_mark_head', 'I'), ('forced_mark_head', 'I')) pyroute2-0.3.5/pyroute2/netlink/rtnl/ifinfmsg.py0000664000175000017500000007500412472732376021641 0ustar peetpeet00000000000000import os import time import json import struct import platform import subprocess from fcntl import ioctl from pyroute2.common import map_namespace from pyroute2.common import ANCIENT # from pyroute2.netlink import NLMSG_ERROR from pyroute2.netlink import nla from pyroute2.netlink import nlmsg from pyroute2.netlink.rtnl.iw_event import iw_event # it's simpler to double constants here, than to change all the # module layout; but it is a subject of the future refactoring RTM_NEWLINK = 16 RTM_DELLINK = 17 # _ANCIENT_BARRIER = 0.3 _BONDING_MASTERS = '/sys/class/net/bonding_masters' _BONDING_SLAVES = '/sys/class/net/%s/bonding/slaves' _BRIDGE_MASTER = '/sys/class/net/%s/brport/bridge/ifindex' _BONDING_MASTER = '/sys/class/net/%s/master/ifindex' IFNAMSIZ = 16 TUNDEV = '/dev/net/tun' arch = platform.machine() if arch == 'x86_64': TUNSETIFF = 0x400454ca TUNSETPERSIST = 0x400454cb TUNSETOWNER = 0x400454cc TUNSETGROUP = 0x400454ce elif arch == 'ppc64': TUNSETIFF = 0x800454ca TUNSETPERSIST = 0x800454cb TUNSETOWNER = 0x800454cc TUNSETGROUP = 0x800454ce else: TUNSETIFF = None ## # # tuntap flags # IFT_TUN = 0x0001 IFT_TAP = 0x0002 IFT_NO_PI = 0x1000 IFT_ONE_QUEUE = 0x2000 IFT_VNET_HDR = 0x4000 IFT_TUN_EXCL = 0x8000 IFT_MULTI_QUEUE = 0x0100 IFT_ATTACH_QUEUE = 0x0200 IFT_DETACH_QUEUE = 0x0400 # read-only IFT_PERSIST = 0x0800 IFT_NOFILTER = 0x1000 ## # # normal flags # IFF_UP = 0x1 # interface is up IFF_BROADCAST = 0x2 # broadcast address valid IFF_DEBUG = 0x4 # turn on debugging IFF_LOOPBACK = 0x8 # is a loopback net IFF_POINTOPOINT = 0x10 # interface is has p-p link IFF_NOTRAILERS = 0x20 # avoid use of trailers IFF_RUNNING = 0x40 # interface RFC2863 OPER_UP IFF_NOARP = 0x80 # no ARP protocol IFF_PROMISC = 0x100 # receive all packets IFF_ALLMULTI = 0x200 # receive all multicast packets IFF_MASTER = 0x400 # master of a load balancer IFF_SLAVE = 0x800 # slave of a load balancer IFF_MULTICAST = 0x1000 # Supports multicast IFF_PORTSEL = 0x2000 # can set media type IFF_AUTOMEDIA = 0x4000 # auto media select active IFF_DYNAMIC = 0x8000 # dialup device with changing addresses IFF_LOWER_UP = 0x10000 # driver signals L1 up IFF_DORMANT = 0x20000 # driver signals dormant IFF_ECHO = 0x40000 # echo sent packets (IFF_NAMES, IFF_VALUES) = map_namespace('IFF', globals()) IFF_MASK = IFF_UP |\ IFF_DEBUG |\ IFF_NOTRAILERS |\ IFF_NOARP |\ IFF_PROMISC |\ IFF_ALLMULTI IFF_VOLATILE = IFF_LOOPBACK |\ IFF_POINTOPOINT |\ IFF_BROADCAST |\ IFF_ECHO |\ IFF_MASTER |\ IFF_SLAVE |\ IFF_RUNNING |\ IFF_LOWER_UP |\ IFF_DORMANT states = ('UNKNOWN', 'NOTPRESENT', 'DOWN', 'LOWERLAYERDOWN', 'TESTING', 'DORMANT', 'UP') state_by_name = dict(((i[1], i[0]) for i in enumerate(states))) state_by_code = dict(enumerate(states)) stats_names = ('rx_packets', 'tx_packets', 'rx_bytes', 'tx_bytes', 'rx_errors', 'tx_errors', 'rx_dropped', 'tx_dropped', 'multicast', 'collisions', 'rx_length_errors', 'rx_over_errors', 'rx_crc_errors', 'rx_frame_errors', 'rx_fifo_errors', 'rx_missed_errors', 'tx_aborted_errors', 'tx_carrier_errors', 'tx_fifo_errors', 'tx_heartbeat_errors', 'tx_window_errors', 'rx_compressed', 'tx_compressed') class ifinfbase(object): ''' Network interface message struct ifinfomsg { unsigned char ifi_family; /* AF_UNSPEC */ unsigned short ifi_type; /* Device type */ int ifi_index; /* Interface index */ unsigned int ifi_flags; /* Device flags */ unsigned int ifi_change; /* change mask */ }; ''' prefix = 'IFLA_' fields = (('family', 'B'), ('__align', 'B'), ('ifi_type', 'H'), ('index', 'i'), ('flags', 'I'), ('change', 'I')) nla_map = (('IFLA_UNSPEC', 'none'), ('IFLA_ADDRESS', 'l2addr'), ('IFLA_BROADCAST', 'l2addr'), ('IFLA_IFNAME', 'asciiz'), ('IFLA_MTU', 'uint32'), ('IFLA_LINK', 'uint32'), ('IFLA_QDISC', 'asciiz'), ('IFLA_STATS', 'ifstats'), ('IFLA_COST', 'hex'), ('IFLA_PRIORITY', 'hex'), ('IFLA_MASTER', 'uint32'), ('IFLA_WIRELESS', 'wireless'), ('IFLA_PROTINFO', 'hex'), ('IFLA_TXQLEN', 'uint32'), ('IFLA_MAP', 'ifmap'), ('IFLA_WEIGHT', 'hex'), ('IFLA_OPERSTATE', 'state'), ('IFLA_LINKMODE', 'uint8'), ('IFLA_LINKINFO', 'ifinfo'), ('IFLA_NET_NS_PID', 'uint32'), ('IFLA_IFALIAS', 'hex'), ('IFLA_NUM_VF', 'uint32'), ('IFLA_VFINFO_LIST', 'hex'), ('IFLA_STATS64', 'ifstats64'), ('IFLA_VF_PORTS', 'hex'), ('IFLA_PORT_SELF', 'hex'), ('IFLA_AF_SPEC', 'af_spec'), ('IFLA_GROUP', 'uint32'), ('IFLA_NET_NS_FD', 'netns_fd'), ('IFLA_EXT_MASK', 'hex'), ('IFLA_PROMISCUITY', 'uint32'), ('IFLA_NUM_TX_QUEUES', 'uint32'), ('IFLA_NUM_RX_QUEUES', 'uint32'), ('IFLA_CARRIER', 'uint8'), ('IFLA_PHYS_PORT_ID', 'hex'), ('IFLA_CARRIER_CHANGES', 'uint32')) @staticmethod def flags2names(flags, mask=0xffffffff): ret = [] for flag in IFF_VALUES: if (flag & mask & flags) == flag: ret.append(IFF_VALUES[flag]) return ret @staticmethod def names2flags(flags): ret = 0 mask = 0 for flag in flags: if flag[0] == '!': flag = flag[1:] else: ret |= IFF_NAMES[flag] mask |= IFF_NAMES[flag] return (ret, mask) def encode(self): # convert flags if isinstance(self['flags'], (set, tuple, list)): self['flags'], self['change'] = self.names2flags(self['flags']) return super(ifinfbase, self).encode() class netns_fd(nla): fields = [('value', 'I')] netns_run_dir = '/var/run/netns' netns_fd = None def encode(self): self.close() # # There are two ways to specify netns # # 1. provide fd to an open file # 2. provide a file name # # In the first case, the value is passed to the kernel # as is. In the second case, the object opens appropriate # file from `self.netns_run_dir` and closes it upon # `__del__(self)` if isinstance(self.value, int): self['value'] = self.value else: self.netns_fd = os.open('%s/%s' % (self.netns_run_dir, self.value), os.O_RDONLY) self['value'] = self.netns_fd nla.encode(self) def __del__(self): self.close() def close(self): if self.netns_fd is not None: os.close(self.netns_fd) class wireless(iw_event): pass class state(nla): fields = (('value', 'B'), ) def encode(self): self['value'] = state_by_name[self.value] nla.encode(self) def decode(self): nla.decode(self) self.value = state_by_code[self['value']] class ifstats(nla): fields = [(i, 'I') for i in stats_names] class ifstats64(nla): fields = [(i, 'Q') for i in stats_names] class ifmap(nla): fields = (('mem_start', 'Q'), ('mem_end', 'Q'), ('base_addr', 'Q'), ('irq', 'H'), ('dma', 'B'), ('port', 'B')) class ifinfo(nla): nla_map = (('IFLA_INFO_UNSPEC', 'none'), ('IFLA_INFO_KIND', 'asciiz'), ('IFLA_INFO_DATA', 'info_data'), ('IFLA_INFO_XSTATS', 'hex'), ('IFLA_INFO_SLAVE_KIND', 'asciiz'), ('IFLA_INFO_SLAVE_DATA', 'info_data')) def info_data(self, *argv, **kwarg): ''' The function returns appropriate IFLA_INFO_DATA type according to IFLA_INFO_KIND info. Return 'hex' type for all unknown kind's and when the kind is not known. ''' kind = self.get_attr('IFLA_INFO_KIND') if kind == 'vlan': return self.vlan_data elif kind == 'bond': return self.bond_data elif kind == 'veth': return self.veth_data elif kind == 'tuntap': return self.tuntap_data elif kind == 'bridge': return self.bridge_data return self.hex class tuntap_data(nla): ''' Fake data type ''' prefix = 'IFTUN_' nla_map = (('IFTUN_UNSPEC', 'none'), ('IFTUN_MODE', 'asciiz'), ('IFTUN_UID', 'uint32'), ('IFTUN_GID', 'uint32'), ('IFTUN_IFR', 'flags')) class flags(nla): fields = (('no_pi', 'B'), ('one_queue', 'B'), ('vnet_hdr', 'B'), ('tun_excl', 'B'), ('multi_queue', 'B'), ('persist', 'B'), ('nofilter', 'B')) class veth_data(nla): nla_map = (('VETH_INFO_UNSPEC', 'none'), ('VETH_INFO_PEER', 'info_peer')) def info_peer(self, *argv, **kwarg): return ifinfveth class vlan_data(nla): nla_map = (('IFLA_VLAN_UNSPEC', 'none'), ('IFLA_VLAN_ID', 'uint16'), ('IFLA_VLAN_FLAGS', 'vlan_flags'), ('IFLA_VLAN_EGRESS_QOS', 'hex'), ('IFLA_VLAN_INGRESS_QOS', 'hex')) class vlan_flags(nla): fields = (('flags', 'I'), ('mask', 'I')) class bridge_data(nla): prefix = 'IFLA_BRIDGE_' nla_map = (('IFLA_BRIDGE_STP_STATE', 'uint32'), ('IFLA_BRIDGE_MAX_AGE', 'uint32')) class bond_data(nla): prefix = 'IFLA_BOND_' nla_map = (('IFLA_BOND_UNSPEC', 'none'), ('IFLA_BOND_MODE', 'uint8'), ('IFLA_BOND_ACTIVE_SLAVE', 'uint32'), ('IFLA_BOND_MIIMON', 'uint32'), ('IFLA_BOND_UPDELAY', 'uint32'), ('IFLA_BOND_DOWNDELAY', 'uint32'), ('IFLA_BOND_USE_CARRIER', 'uint8'), ('IFLA_BOND_ARP_INTERVAL', 'uint32'), ('IFLA_BOND_ARP_IP_TARGET', 'arp_ip_target'), ('IFLA_BOND_ARP_VALIDATE', 'uint32'), ('IFLA_BOND_ARP_ALL_TARGETS', 'uint32'), ('IFLA_BOND_PRIMARY', 'uint32'), ('IFLA_BOND_PRIMARY_RESELECT', 'uint8'), ('IFLA_BOND_FAIL_OVER_MAC', 'uint8'), ('IFLA_BOND_XMIT_HASH_POLICY', 'uint8'), ('IFLA_BOND_RESEND_IGMP', 'uint32'), ('IFLA_BOND_NUM_PEER_NOTIF', 'uint8'), ('IFLA_BOND_ALL_SLAVES_ACTIVE', 'uint8'), ('IFLA_BOND_MIN_LINKS', 'uint32'), ('IFLA_BOND_LP_INTERVAL', 'uint32'), ('IFLA_BOND_PACKETS_PER_SLAVE', 'uint32'), ('IFLA_BOND_AD_LACP_RATE', 'uint8'), ('IFLA_BOND_AD_SELECT', 'uint8'), ('IFLA_BOND_AD_INFO', 'ad_info')) class ad_info(nla): nla_map = (('IFLA_BOND_AD_INFO_UNSPEC', 'none'), ('IFLA_BOND_AD_INFO_AGGREGATOR', 'uint16'), ('IFLA_BOND_AD_INFO_NUM_PORTS', 'uint16'), ('IFLA_BOND_AD_INFO_ACTOR_KEY', 'uint16'), ('IFLA_BOND_AD_INFO_PARTNER_KEY', 'uint16'), ('IFLA_BOND_AD_INFO_PARTNER_MAC', 'l2addr')) class arp_ip_target(nla): fields = (('targets', '16I'), ) class af_spec(nla): nla_map = (('AF_UNSPEC', 'none'), ('AF_UNIX', 'hex'), ('AF_INET', 'inet'), ('AF_AX25', 'hex'), ('AF_IPX', 'hex'), ('AF_APPLETALK', 'hex'), ('AF_NETROM', 'hex'), ('AF_BRIDGE', 'hex'), ('AF_ATMPVC', 'hex'), ('AF_X25', 'hex'), ('AF_INET6', 'inet6')) class inet(nla): # ./include/linux/inetdevice.h: struct ipv4_devconf field_names = ('sysctl', 'forwarding', 'mc_forwarding', 'proxy_arp', 'accept_redirects', 'secure_redirects', 'send_redirects', 'shared_media', 'rp_filter', 'accept_source_route', 'bootp_relay', 'log_martians', 'tag', 'arp_filter', 'medium_id', 'disable_xfrm', 'disable_policy', 'force_igmp_version', 'arp_announce', 'arp_ignore', 'promote_secondaries', 'arp_accept', 'arp_notify', 'accept_local', 'src_valid_mark', 'proxy_arp_pvlan', 'route_localnet') fields = [(i, 'I') for i in field_names] class inet6(nla): nla_map = (('IFLA_INET6_UNSPEC', 'none'), ('IFLA_INET6_FLAGS', 'uint32'), ('IFLA_INET6_CONF', 'ipv6_devconf'), ('IFLA_INET6_STATS', 'ipv6_stats'), ('IFLA_INET6_MCAST', 'hex'), ('IFLA_INET6_CACHEINFO', 'ipv6_cache_info'), ('IFLA_INET6_ICMP6STATS', 'icmp6_stats'), ('IFLA_INET6_TOKEN', 'ip6addr'), ('IFLA_INET6_ADDR_GEN_MODE', 'uint8')) class ipv6_devconf(nla): # ./include/uapi/linux/ipv6.h # DEVCONF_ field_names = ('forwarding', 'hop_limit', 'mtu', 'accept_ra', 'accept_redirects', 'autoconf', 'dad_transmits', 'router_solicitations', 'router_solicitation_interval', 'router_solicitation_delay', 'use_tempaddr', 'temp_valid_lft', 'temp_prefered_lft', 'regen_max_retry', 'max_desync_factor', 'max_addresses', 'force_mld_version', 'accept_ra_defrtr', 'accept_ra_pinfo', 'accept_ra_rtr_pref', 'router_probe_interval', 'accept_ra_rt_info_max_plen', 'proxy_ndp', 'optimistic_dad', 'accept_source_route', 'mc_forwarding', 'disable_ipv6', 'accept_dad', 'force_tllao', 'ndisc_notify') fields = [(i, 'I') for i in field_names] class ipv6_cache_info(nla): # ./include/uapi/linux/if_link.h: struct ifla_cacheinfo fields = (('max_reasm_len', 'I'), ('tstamp', 'I'), ('reachable_time', 'I'), ('retrans_time', 'I')) class ipv6_stats(nla): field_names = ('inoctets', 'fragcreates', 'indiscards', 'num', 'outoctets', 'outnoroutes', 'inbcastoctets', 'outforwdatagrams', 'outpkts', 'reasmtimeout', 'inhdrerrors', 'reasmreqds', 'fragfails', 'outmcastpkts', 'inaddrerrors', 'inmcastpkts', 'reasmfails', 'outdiscards', 'outbcastoctets', 'inmcastoctets', 'inpkts', 'fragoks', 'intoobigerrors', 'inunknownprotos', 'intruncatedpkts', 'outbcastpkts', 'reasmoks', 'inbcastpkts', 'innoroutes', 'indelivers', 'outmcastoctets') fields = [(i, 'I') for i in field_names] class icmp6_stats(nla): fields = (('num', 'Q'), ('inerrors', 'Q'), ('outmsgs', 'Q'), ('outerrors', 'Q'), ('inmsgs', 'Q')) class ifinfmsg(ifinfbase, nlmsg): pass class ifinfveth(ifinfbase, nla): pass def proxy_linkinfo(data, nl): offset = 0 inbox = [] while offset < len(data): msg = ifinfmsg(data[offset:]) msg.decode() inbox.append(msg) offset += msg['header']['length'] data = b'' for msg in inbox: kind = None ifname = msg.get_attr('IFLA_IFNAME') # fix master if ANCIENT: master = compat_get_master(ifname) if master is not None: msg['attrs'].append(['IFLA_MASTER', master]) # fix linkinfo & kind li = msg.get_attr('IFLA_LINKINFO') if li is not None: kind = li.get_attr('IFLA_INFO_KIND') if kind is None: kind = get_interface_type(ifname) li['attrs'].append(['IFLA_INFO_KIND', kind]) else: kind = get_interface_type(ifname) msg['attrs'].append(['IFLA_LINKINFO', {'attrs': [['IFLA_INFO_KIND', kind]]}]) li = msg.get_attr('IFLA_LINKINFO') # fetch specific interface data if (kind in ('bridge', 'bond')) and \ [x for x in li['attrs'] if x[0] == 'IFLA_INFO_DATA']: if kind == 'bridge': t = '/sys/class/net/%s/bridge/%s' ifdata = ifinfmsg.ifinfo.bridge_data elif kind == 'bond': t = '/sys/class/net/%s/bonding/%s' ifdata = ifinfmsg.ifinfo.bond_data commands = [] for cmd, _ in ifdata.nla_map: try: with open(t % (ifname, ifdata.nla2name(cmd)), 'r') as f: value = f.read() if cmd == 'IFLA_BOND_MODE': value = value.split()[1] commands.append([cmd, int(value)]) except: pass if commands: li['attrs'].append(['IFLA_INFO_DATA', {'attrs': commands}]) msg.reset() msg.encode() data += msg.buf.getvalue() return {'verdict': 'forward', 'data': data} def proxy_setlink(data, nl): def get_interface(index): msg = nl.get_links(index)[0] return {'ifname': msg.get_attr('IFLA_IFNAME'), 'master': msg.get_attr('IFLA_MASTER'), 'kind': msg. get_attr('IFLA_LINKINFO'). get_attr('IFLA_INFO_KIND')} msg = ifinfmsg(data) msg.decode() forward = True kind = None infodata = None ifname = msg.get_attr('IFLA_IFNAME') or \ get_interface(msg['index'])['ifname'] linkinfo = msg.get_attr('IFLA_LINKINFO') if linkinfo: kind = linkinfo.get_attr('IFLA_INFO_KIND') infodata = linkinfo.get_attr('IFLA_INFO_DATA') if kind in ('bond', 'bridge'): code = 0 # if kind == 'bond': func = compat_set_bond elif kind == 'bridge': func = compat_set_bridge # for (cmd, value) in infodata.get('attrs', []): cmd = infodata.nla2name(cmd) code = func(ifname, cmd, value) or code # if code: err = OSError() err.errno = code raise err # is it a port setup? master = msg.get_attr('IFLA_MASTER') if master is not None: if master == 0: # port delete # 1. get the current master iface = get_interface(msg['index']) master = get_interface(iface['master']) cmd = 'del' else: # port add # 1. get the master master = get_interface(master) cmd = 'add' # 2. delete the port if master['kind'] == 'team': forward = manage_team_port(cmd, master['ifname'], ifname) elif master['kind'] == 'bridge': forward = compat_bridge_port(cmd, master['ifname'], ifname) elif master['kind'] == 'bond': forward = compat_bond_port(cmd, master['ifname'], ifname) if forward is not None: return {'verdict': 'forward', 'data': data} def proxy_dellink(data, nl): orig_msg = ifinfmsg(data) orig_msg.decode() # get full interface description msg = nl.get_links(orig_msg['index'])[0] msg['header']['type'] = orig_msg['header']['type'] # get the interface kind kind = None li = msg.get_attr('IFLA_LINKINFO') if li is not None: kind = li.get_attr('IFLA_INFO_KIND') if kind in ('ovs-bridge', 'openvswitch'): return manage_ovs(msg) if ANCIENT and kind in ('bridge', 'bond'): # route the request if kind == 'bridge': compat_del_bridge(msg.get_attr('IFLA_IFNAME')) elif kind == 'bond': compat_del_bond(msg.get_attr('IFLA_IFNAME')) # while RTM_NEWLINK is not intercepted -- sleep time.sleep(_ANCIENT_BARRIER) return return {'verdict': 'forward', 'data': data} def proxy_newlink(data, nl): msg = ifinfmsg(data) msg.decode() kind = None # get the interface kind linkinfo = msg.get_attr('IFLA_LINKINFO') if linkinfo is not None: kind = [x[1] for x in linkinfo['attrs'] if x[0] == 'IFLA_INFO_KIND'] if kind: kind = kind[0] if kind == 'tuntap': return manage_tuntap(msg) elif kind == 'team': return manage_team(msg) elif kind in ('ovs-bridge', 'openvswitch'): return manage_ovs(msg) if ANCIENT and kind in ('bridge', 'bond'): # route the request if kind == 'bridge': compat_create_bridge(msg.get_attr('IFLA_IFNAME')) elif kind == 'bond': compat_create_bond(msg.get_attr('IFLA_IFNAME')) # while RTM_NEWLINK is not intercepted -- sleep time.sleep(_ANCIENT_BARRIER) return return {'verdict': 'forward', 'data': data} def manage_team(msg): assert msg['header']['type'] == RTM_NEWLINK config = {'device': msg.get_attr('IFLA_IFNAME'), 'runner': {'name': 'activebackup'}, 'link_watch': {'name': 'ethtool'}} with open(os.devnull, 'w') as fnull: subprocess.check_call(['teamd', '-d', '-n', '-c', json.dumps(config)], stdout=fnull, stderr=fnull) def manage_team_port(cmd, master, ifname): remap = {'add': 'add', 'del': 'remove'} cmd = remap[cmd] with open(os.devnull, 'w') as fnull: subprocess.check_call(['teamdctl', master, 'port', cmd, ifname], stdout=fnull, stderr=fnull) # do NOT forward request after return False def manage_ovs(msg): linkinfo = msg.get_attr('IFLA_LINKINFO') ifname = msg.get_attr('IFLA_IFNAME') kind = linkinfo.get_attr('IFLA_INFO_KIND') # operations map op_map = {RTM_NEWLINK: {'ovs-bridge': 'add-br', 'openvswitch': 'add-br'}, RTM_DELLINK: {'ovs-bridge': 'del-br', 'openvswitch': 'del-br'}} op = op_map[msg['header']['type']][kind] # make a call with open(os.devnull, 'w') as fnull: subprocess.check_call(['ovs-vsctl', op, ifname], stdout=fnull, stderr=fnull) def manage_tuntap(msg): if TUNSETIFF is None: raise Exception('unsupported arch') if msg['header']['type'] != RTM_NEWLINK: raise Exception('unsupported event') ifru_flags = 0 linkinfo = msg.get_attr('IFLA_LINKINFO') infodata = linkinfo.get_attr('IFLA_INFO_DATA') flags = infodata.get_attr('IFTUN_IFR', None) if infodata.get_attr('IFTUN_MODE') == 'tun': ifru_flags |= IFT_TUN elif infodata.get_attr('IFTUN_MODE') == 'tap': ifru_flags |= IFT_TAP else: raise ValueError('invalid mode') if flags is not None: if flags['no_pi']: ifru_flags |= IFT_NO_PI if flags['one_queue']: ifru_flags |= IFT_ONE_QUEUE if flags['vnet_hdr']: ifru_flags |= IFT_VNET_HDR if flags['multi_queue']: ifru_flags |= IFT_MULTI_QUEUE ifr = msg.get_attr('IFLA_IFNAME') if len(ifr) > IFNAMSIZ: raise ValueError('ifname too long') ifr += (IFNAMSIZ - len(ifr)) * '\0' ifr = ifr.encode('ascii') ifr += struct.pack('H', ifru_flags) user = infodata.get_attr('IFTUN_UID') group = infodata.get_attr('IFTUN_GID') # fd = os.open(TUNDEV, os.O_RDWR) try: ioctl(fd, TUNSETIFF, ifr) if user is not None: ioctl(fd, TUNSETOWNER, user) if group is not None: ioctl(fd, TUNSETGROUP, group) ioctl(fd, TUNSETPERSIST, 1) except Exception: raise finally: os.close(fd) def compat_create_bridge(name): with open(os.devnull, 'w') as fnull: subprocess.check_call(['brctl', 'addbr', name], stdout=fnull, stderr=fnull) def compat_create_bond(name): with open(_BONDING_MASTERS, 'w') as f: f.write('+%s' % (name)) def compat_set_bond(name, cmd, value): # FIXME: join with bridge # FIXME: use internal IO, not bash t = 'echo %s >/sys/class/net/%s/bonding/%s' with open(os.devnull, 'w') as fnull: return subprocess.call(['bash', '-c', t % (value, name, cmd)], stdout=fnull, stderr=fnull) def compat_set_bridge(name, cmd, value): t = 'echo %s >/sys/class/net/%s/bridge/%s' with open(os.devnull, 'w') as fnull: return subprocess.call(['bash', '-c', t % (value, name, cmd)], stdout=fnull, stderr=fnull) def compat_del_bridge(name): with open(os.devnull, 'w') as fnull: subprocess.check_call(['ip', 'link', 'set', 'dev', name, 'down']) subprocess.check_call(['brctl', 'delbr', name], stdout=fnull, stderr=fnull) def compat_del_bond(name): subprocess.check_call(['ip', 'link', 'set', 'dev', name, 'down']) with open(_BONDING_MASTERS, 'w') as f: f.write('-%s' % (name)) def compat_bridge_port(cmd, master, port): if not ANCIENT: return True with open(os.devnull, 'w') as fnull: subprocess.check_call(['brctl', '%sif' % (cmd), master, port], stdout=fnull, stderr=fnull) def compat_bond_port(cmd, master, port): if not ANCIENT: return True remap = {'add': '+', 'del': '-'} cmd = remap[cmd] with open(_BONDING_SLAVES % (master), 'w') as f: f.write('%s%s' % (cmd, port)) def compat_get_master(name): f = None for i in (_BRIDGE_MASTER, _BONDING_MASTER): try: f = open(i % (name)) break except IOError: pass if f is not None: master = int(f.read()) f.close() return master def get_interface_type(name): ''' Utility function to get interface type. Unfortunately, we can not rely on RTNL or even ioctl(). RHEL doesn't support interface type in RTNL and doesn't provide extended (private) interface flags via ioctl(). Args: * name (str): interface name Returns: * False -- sysfs info unavailable * None -- type not known * str -- interface type: * 'bond' * 'bridge' ''' # FIXME: support all interface types? Right now it is # not needed try: ifattrs = os.listdir('/sys/class/net/%s/' % (name)) except OSError as e: if e.errno == 2: return 'unknown' else: raise if 'bonding' in ifattrs: return 'bond' elif 'bridge' in ifattrs: return 'bridge' else: return 'unknown' pyroute2-0.3.5/pyroute2/netlink/rtnl/dhcpmsg.py0000664000175000017500000000116112444764006021447 0ustar peetpeet00000000000000from pyroute2.netlink import nla from pyroute2.netlink import nlmsg class dhcpmsg(nlmsg): ''' Custom message type Option for a DHCP agent ''' prefix = 'DHCP_' fields = (('family', 'H'), ('prefixlen', 'H'), ('index', 'I')) nla_map = (('DHCP_UNSPEC', 'none'), ('DHCP_ADDRESS', 'ipaddr'), ('DHCP_IFNAME', 'asciiz'), ('DHCP_AGENTINFO', 'agentinfo')) class agentinfo(nla): nla_map = (('DHCP_AGENT', 'asciiz'), ('DHCP_AGENT_PID', 'uint32'), ('DHCP_AGENT_STATUS', 'asciiz')) pyroute2-0.3.5/pyroute2/netlink/nl80211/0000775000175000017500000000000012474420674017502 5ustar peetpeet00000000000000pyroute2-0.3.5/pyroute2/netlink/nl80211/__init__.py0000664000175000017500000006341712472731760021625 0ustar peetpeet00000000000000''' NL80211 module ================ TODO ''' from pyroute2.common import map_namespace from pyroute2.netlink import genlmsg from pyroute2.netlink.generic import GenericNetlinkSocket from pyroute2.netlink.nlsocket import Marshal from pyroute2.netlink import nla from pyroute2.netlink import nla_base # import pdb import struct from pyroute2.common import hexdump # nl80211 commands NL80211_CMD_UNSPEC = 0 NL80211_CMD_GET_WIPHY = 1 NL80211_CMD_SET_WIPHY = 2 NL80211_CMD_NEW_WIPHY = 3 NL80211_CMD_DEL_WIPHY = 4 NL80211_CMD_GET_INTERFACE = 5 NL80211_CMD_SET_INTERFACE = 6 NL80211_CMD_NEW_INTERFACE = 7 NL80211_CMD_DEL_INTERFACE = 8 NL80211_CMD_GET_KEY = 9 NL80211_CMD_SET_KEY = 10 NL80211_CMD_NEW_KEY = 11 NL80211_CMD_DEL_KEY = 12 NL80211_CMD_GET_BEACON = 13 NL80211_CMD_SET_BEACON = 14 NL80211_CMD_START_AP = 15 NL80211_CMD_NEW_BEACON = NL80211_CMD_START_AP NL80211_CMD_STOP_AP = 16 NL80211_CMD_DEL_BEACON = NL80211_CMD_STOP_AP NL80211_CMD_GET_STATION = 17 NL80211_CMD_SET_STATION = 18 NL80211_CMD_NEW_STATION = 19 NL80211_CMD_DEL_STATION = 20 NL80211_CMD_GET_MPATH = 21 NL80211_CMD_SET_MPATH = 22 NL80211_CMD_NEW_MPATH = 23 NL80211_CMD_DEL_MPATH = 24 NL80211_CMD_SET_BSS = 25 NL80211_CMD_SET_REG = 26 NL80211_CMD_REQ_SET_REG = 27 NL80211_CMD_GET_MESH_CONFIG = 28 NL80211_CMD_SET_MESH_CONFIG = 29 NL80211_CMD_SET_MGMT_EXTRA_IE = 30 NL80211_CMD_GET_REG = 31 NL80211_CMD_GET_SCAN = 32 NL80211_CMD_TRIGGER_SCAN = 33 NL80211_CMD_NEW_SCAN_RESULTS = 34 NL80211_CMD_SCAN_ABORTED = 35 NL80211_CMD_REG_CHANGE = 36 NL80211_CMD_AUTHENTICATE = 37 NL80211_CMD_ASSOCIATE = 38 NL80211_CMD_DEAUTHENTICATE = 39 NL80211_CMD_DISASSOCIATE = 40 NL80211_CMD_MICHAEL_MIC_FAILURE = 41 NL80211_CMD_REG_BEACON_HINT = 42 NL80211_CMD_JOIN_IBSS = 43 NL80211_CMD_LEAVE_IBSS = 44 NL80211_CMD_TESTMODE = 45 NL80211_CMD_CONNECT = 46 NL80211_CMD_ROAM = 47 NL80211_CMD_DISCONNECT = 48 NL80211_CMD_SET_WIPHY_NETNS = 49 NL80211_CMD_GET_SURVEY = 50 NL80211_CMD_NEW_SURVEY_RESULTS = 51 NL80211_CMD_SET_PMKSA = 52 NL80211_CMD_DEL_PMKSA = 53 NL80211_CMD_FLUSH_PMKSA = 54 NL80211_CMD_REMAIN_ON_CHANNEL = 55 NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL = 56 NL80211_CMD_SET_TX_BITRATE_MASK = 57 NL80211_CMD_REGISTER_FRAME = 58 NL80211_CMD_REGISTER_ACTION = NL80211_CMD_REGISTER_FRAME NL80211_CMD_FRAME = 59 NL80211_CMD_ACTION = NL80211_CMD_FRAME NL80211_CMD_FRAME_TX_STATUS = 60 NL80211_CMD_ACTION_TX_STATUS = NL80211_CMD_FRAME_TX_STATUS NL80211_CMD_SET_POWER_SAVE = 61 NL80211_CMD_GET_POWER_SAVE = 62 NL80211_CMD_SET_CQM = 63 NL80211_CMD_NOTIFY_CQM = 64 NL80211_CMD_SET_CHANNEL = 65 NL80211_CMD_SET_WDS_PEER = 66 NL80211_CMD_FRAME_WAIT_CANCEL = 67 NL80211_CMD_JOIN_MESH = 68 NL80211_CMD_LEAVE_MESH = 69 NL80211_CMD_UNPROT_DEAUTHENTICATE = 70 NL80211_CMD_UNPROT_DISASSOCIATE = 71 NL80211_CMD_NEW_PEER_CANDIDATE = 72 NL80211_CMD_GET_WOWLAN = 73 NL80211_CMD_SET_WOWLAN = 74 NL80211_CMD_START_SCHED_SCAN = 75 NL80211_CMD_STOP_SCHED_SCAN = 76 NL80211_CMD_SCHED_SCAN_RESULTS = 77 NL80211_CMD_SCHED_SCAN_STOPPED = 78 NL80211_CMD_SET_REKEY_OFFLOAD = 79 NL80211_CMD_PMKSA_CANDIDATE = 80 NL80211_CMD_TDLS_OPER = 81 NL80211_CMD_TDLS_MGMT = 82 NL80211_CMD_UNEXPECTED_FRAME = 83 NL80211_CMD_PROBE_CLIENT = 84 NL80211_CMD_REGISTER_BEACONS = 85 NL80211_CMD_UNEXPECTED_4ADDR_FRAME = 86 NL80211_CMD_SET_NOACK_MAP = 87 NL80211_CMD_CH_SWITCH_NOTIFY = 88 NL80211_CMD_START_P2P_DEVICE = 89 NL80211_CMD_STOP_P2P_DEVICE = 90 NL80211_CMD_CONN_FAILED = 91 NL80211_CMD_SET_MCAST_RATE = 92 NL80211_CMD_SET_MAC_ACL = 93 NL80211_CMD_RADAR_DETECT = 94 NL80211_CMD_GET_PROTOCOL_FEATURES = 95 NL80211_CMD_UPDATE_FT_IES = 96 NL80211_CMD_FT_EVENT = 97 NL80211_CMD_CRIT_PROTOCOL_START = 98 NL80211_CMD_CRIT_PROTOCOL_STOP = 99 NL80211_CMD_GET_COALESCE = 100 NL80211_CMD_SET_COALESCE = 101 NL80211_CMD_CHANNEL_SWITCH = 102 NL80211_CMD_VENDOR = 103 NL80211_CMD_SET_QOS_MAP = 104 NL80211_CMD_ADD_TX_TS = 105 NL80211_CMD_DEL_TX_TS = 106 NL80211_CMD_GET_MPP = 107 NL80211_CMD_JOIN_OCB = 108 NL80211_CMD_LEAVE_OCB = 109 NL80211_CMD_CH_SWITCH_STARTED_NOTIFY = 110 NL80211_CMD_TDLS_CHANNEL_SWITCH = 111 NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH = 112 NL80211_CMD_WIPHY_REG_CHANGE = 113 NL80211_CMD_MAX = NL80211_CMD_WIPHY_REG_CHANGE (NL80211_NAMES, NL80211_VALUES) = map_namespace('NL80211_CMD_', globals()) NL80211_BSS_ELEMENTS_SSID = 0 NL80211_BSS_ELEMENTS_SUPPORTED_RATES = 1 NL80211_BSS_ELEMENTS_CHANNEL = 3 NL80211_BSS_ELEMENTS_VENDOR = 221 BSS_MEMBERSHIP_SELECTOR_HT_PHY = 127 BSS_MEMBERSHIP_SELECTOR_VHT_PHY = 126 class nl80211cmd(genlmsg): nla_map = (('NL80211_ATTR_UNSPEC', 'none'), ('NL80211_ATTR_WIPHY', 'hex'), ('NL80211_ATTR_WIPHY_NAME', 'asciiz'), ('NL80211_ATTR_IFINDEX', 'uint32'), ('NL80211_ATTR_IFNAME', 'asciiz'), ('NL80211_ATTR_IFTYPE', 'hex'), ('NL80211_ATTR_MAC', 'l2addr'), ('NL80211_ATTR_KEY_DATA', 'hex'), ('NL80211_ATTR_KEY_IDX', 'hex'), ('NL80211_ATTR_KEY_CIPHER', 'uint32'), ('NL80211_ATTR_KEY_SEQ', 'hex'), ('NL80211_ATTR_KEY_DEFAULT', 'hex'), ('NL80211_ATTR_BEACON_INTERVAL', 'hex'), ('NL80211_ATTR_DTIM_PERIOD', 'hex'), ('NL80211_ATTR_BEACON_HEAD', 'hex'), ('NL80211_ATTR_BEACON_TAIL', 'hex'), ('NL80211_ATTR_STA_AID', 'hex'), ('NL80211_ATTR_STA_FLAGS', 'hex'), ('NL80211_ATTR_STA_LISTEN_INTERVAL', 'hex'), ('NL80211_ATTR_STA_SUPPORTED_RATES', 'hex'), ('NL80211_ATTR_STA_VLAN', 'hex'), ('NL80211_ATTR_STA_INFO', 'hex'), ('NL80211_ATTR_WIPHY_BANDS', 'hex'), ('NL80211_ATTR_MNTR_FLAGS', 'hex'), ('NL80211_ATTR_MESH_ID', 'hex'), ('NL80211_ATTR_STA_PLINK_ACTION', 'hex'), ('NL80211_ATTR_MPATH_NEXT_HOP', 'hex'), ('NL80211_ATTR_MPATH_INFO', 'hex'), ('NL80211_ATTR_BSS_CTS_PROT', 'hex'), ('NL80211_ATTR_BSS_SHORT_PREAMBLE', 'hex'), ('NL80211_ATTR_BSS_SHORT_SLOT_TIME', 'hex'), ('NL80211_ATTR_HT_CAPABILITY', 'hex'), ('NL80211_ATTR_SUPPORTED_IFTYPES', 'hex'), ('NL80211_ATTR_REG_ALPHA2', 'hex'), ('NL80211_ATTR_REG_RULES', 'hex'), ('NL80211_ATTR_MESH_CONFIG', 'hex'), ('NL80211_ATTR_BSS_BASIC_RATES', 'hex'), ('NL80211_ATTR_WIPHY_TXQ_PARAMS', 'hex'), ('NL80211_ATTR_WIPHY_FREQ', 'hex'), ('NL80211_ATTR_WIPHY_CHANNEL_TYPE', 'hex'), ('NL80211_ATTR_KEY_DEFAULT_MGMT', 'hex'), ('NL80211_ATTR_MGMT_SUBTYPE', 'hex'), ('NL80211_ATTR_IE', 'hex'), ('NL80211_ATTR_MAX_NUM_SCAN_SSIDS', 'hex'), ('NL80211_ATTR_SCAN_FREQUENCIES', 'hex'), ('NL80211_ATTR_SCAN_SSIDS', 'hex'), ('NL80211_ATTR_GENERATION', 'hex'), ('NL80211_ATTR_BSS', 'bss'), ('NL80211_ATTR_REG_INITIATOR', 'hex'), ('NL80211_ATTR_REG_TYPE', 'hex'), ('NL80211_ATTR_SUPPORTED_COMMANDS', 'hex'), ('NL80211_ATTR_FRAME', 'hex'), ('NL80211_ATTR_SSID', 'hex'), ('NL80211_ATTR_AUTH_TYPE', 'hex'), ('NL80211_ATTR_REASON_CODE', 'hex'), ('NL80211_ATTR_KEY_TYPE', 'hex'), ('NL80211_ATTR_MAX_SCAN_IE_LEN', 'hex'), ('NL80211_ATTR_CIPHER_SUITES', 'hex'), ('NL80211_ATTR_FREQ_BEFORE', 'hex'), ('NL80211_ATTR_FREQ_AFTER', 'hex'), ('NL80211_ATTR_FREQ_FIXED', 'hex'), ('NL80211_ATTR_WIPHY_RETRY_SHORT', 'hex'), ('NL80211_ATTR_WIPHY_RETRY_LONG', 'hex'), ('NL80211_ATTR_WIPHY_FRAG_THRESHOLD', 'hex'), ('NL80211_ATTR_WIPHY_RTS_THRESHOLD', 'hex'), ('NL80211_ATTR_TIMED_OUT', 'hex'), ('NL80211_ATTR_USE_MFP', 'hex'), ('NL80211_ATTR_STA_FLAGS2', 'hex'), ('NL80211_ATTR_CONTROL_PORT', 'hex'), ('NL80211_ATTR_TESTDATA', 'hex'), ('NL80211_ATTR_PRIVACY', 'hex'), ('NL80211_ATTR_DISCONNECTED_BY_AP', 'hex'), ('NL80211_ATTR_STATUS_CODE', 'hex'), ('NL80211_ATTR_CIPHER_SUITES_PAIRWISE', 'hex'), ('NL80211_ATTR_CIPHER_SUITE_GROUP', 'hex'), ('NL80211_ATTR_WPA_VERSIONS', 'hex'), ('NL80211_ATTR_AKM_SUITES', 'hex'), ('NL80211_ATTR_REQ_IE', 'hex'), ('NL80211_ATTR_RESP_IE', 'hex'), ('NL80211_ATTR_PREV_BSSID', 'hex'), ('NL80211_ATTR_KEY', 'hex'), ('NL80211_ATTR_KEYS', 'hex'), ('NL80211_ATTR_PID', 'hex'), ('NL80211_ATTR_4ADDR', 'hex'), ('NL80211_ATTR_SURVEY_INFO', 'hex'), ('NL80211_ATTR_PMKID', 'hex'), ('NL80211_ATTR_MAX_NUM_PMKIDS', 'hex'), ('NL80211_ATTR_DURATION', 'hex'), ('NL80211_ATTR_COOKIE', 'hex'), ('NL80211_ATTR_WIPHY_COVERAGE_CLASS', 'hex'), ('NL80211_ATTR_TX_RATES', 'hex'), ('NL80211_ATTR_FRAME_MATCH', 'hex'), ('NL80211_ATTR_ACK', 'hex'), ('NL80211_ATTR_PS_STATE', 'hex'), ('NL80211_ATTR_CQM', 'hex'), ('NL80211_ATTR_LOCAL_STATE_CHANGE', 'hex'), ('NL80211_ATTR_AP_ISOLATE', 'hex'), ('NL80211_ATTR_WIPHY_TX_POWER_SETTING', 'hex'), ('NL80211_ATTR_WIPHY_TX_POWER_LEVEL', 'hex'), ('NL80211_ATTR_TX_FRAME_TYPES', 'hex'), ('NL80211_ATTR_RX_FRAME_TYPES', 'hex'), ('NL80211_ATTR_FRAME_TYPE', 'hex'), ('NL80211_ATTR_CONTROL_PORT_ETHERTYPE', 'hex'), ('NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT', 'hex'), ('NL80211_ATTR_SUPPORT_IBSS_RSN', 'hex'), ('NL80211_ATTR_WIPHY_ANTENNA_TX', 'hex'), ('NL80211_ATTR_WIPHY_ANTENNA_RX', 'hex'), ('NL80211_ATTR_MCAST_RATE', 'hex'), ('NL80211_ATTR_OFFCHANNEL_TX_OK', 'hex'), ('NL80211_ATTR_BSS_HT_OPMODE', 'hex'), ('NL80211_ATTR_KEY_DEFAULT_TYPES', 'hex'), ('NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION', 'hex'), ('NL80211_ATTR_MESH_SETUP', 'hex'), ('NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX', 'hex'), ('NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX', 'hex'), ('NL80211_ATTR_SUPPORT_MESH_AUTH', 'hex'), ('NL80211_ATTR_STA_PLINK_STATE', 'hex'), ('NL80211_ATTR_WOWLAN_TRIGGERS', 'hex'), ('NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED', 'hex'), ('NL80211_ATTR_SCHED_SCAN_INTERVAL', 'hex'), ('NL80211_ATTR_INTERFACE_COMBINATIONS', 'hex'), ('NL80211_ATTR_SOFTWARE_IFTYPES', 'hex'), ('NL80211_ATTR_REKEY_DATA', 'hex'), ('NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS', 'hex'), ('NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN', 'hex'), ('NL80211_ATTR_SCAN_SUPP_RATES', 'hex'), ('NL80211_ATTR_HIDDEN_SSID', 'hex'), ('NL80211_ATTR_IE_PROBE_RESP', 'hex'), ('NL80211_ATTR_IE_ASSOC_RESP', 'hex'), ('NL80211_ATTR_STA_WME', 'hex'), ('NL80211_ATTR_SUPPORT_AP_UAPSD', 'hex'), ('NL80211_ATTR_ROAM_SUPPORT', 'hex'), ('NL80211_ATTR_SCHED_SCAN_MATCH', 'hex'), ('NL80211_ATTR_MAX_MATCH_SETS', 'hex'), ('NL80211_ATTR_PMKSA_CANDIDATE', 'hex'), ('NL80211_ATTR_TX_NO_CCK_RATE', 'hex'), ('NL80211_ATTR_TDLS_ACTION', 'hex'), ('NL80211_ATTR_TDLS_DIALOG_TOKEN', 'hex'), ('NL80211_ATTR_TDLS_OPERATION', 'hex'), ('NL80211_ATTR_TDLS_SUPPORT', 'hex'), ('NL80211_ATTR_TDLS_EXTERNAL_SETUP', 'hex'), ('NL80211_ATTR_DEVICE_AP_SME', 'hex'), ('NL80211_ATTR_DONT_WAIT_FOR_ACK', 'hex'), ('NL80211_ATTR_FEATURE_FLAGS', 'hex'), ('NL80211_ATTR_PROBE_RESP_OFFLOAD', 'hex'), ('NL80211_ATTR_PROBE_RESP', 'hex'), ('NL80211_ATTR_DFS_REGION', 'hex'), ('NL80211_ATTR_DISABLE_HT', 'hex'), ('NL80211_ATTR_HT_CAPABILITY_MASK', 'hex'), ('NL80211_ATTR_NOACK_MAP', 'hex'), ('NL80211_ATTR_INACTIVITY_TIMEOUT', 'hex'), ('NL80211_ATTR_RX_SIGNAL_DBM', 'hex'), ('NL80211_ATTR_BG_SCAN_PERIOD', 'hex'), ('NL80211_ATTR_WDEV', 'uint32'), ('NL80211_ATTR_USER_REG_HINT_TYPE', 'hex'), ('NL80211_ATTR_CONN_FAILED_REASON', 'hex'), ('NL80211_ATTR_SAE_DATA', 'hex'), ('NL80211_ATTR_VHT_CAPABILITY', 'hex'), ('NL80211_ATTR_SCAN_FLAGS', 'hex'), ('NL80211_ATTR_CHANNEL_WIDTH', 'uint32'), ('NL80211_ATTR_CENTER_FREQ1', 'hex'), ('NL80211_ATTR_CENTER_FREQ2', 'hex'), ('NL80211_ATTR_P2P_CTWINDOW', 'hex'), ('NL80211_ATTR_P2P_OPPPS', 'hex'), ('NL80211_ATTR_LOCAL_MESH_POWER_MODE', 'hex'), ('NL80211_ATTR_ACL_POLICY', 'hex'), ('NL80211_ATTR_MAC_ADDRS', 'hex'), ('NL80211_ATTR_MAC_ACL_MAX', 'hex'), ('NL80211_ATTR_RADAR_EVENT', 'hex'), ('NL80211_ATTR_EXT_CAPA', 'hex'), ('NL80211_ATTR_EXT_CAPA_MASK', 'hex'), ('NL80211_ATTR_STA_CAPABILITY', 'hex'), ('NL80211_ATTR_STA_EXT_CAPABILITY', 'hex'), ('NL80211_ATTR_PROTOCOL_FEATURES', 'hex'), ('NL80211_ATTR_SPLIT_WIPHY_DUMP', 'hex'), ('NL80211_ATTR_DISABLE_VHT', 'hex'), ('NL80211_ATTR_VHT_CAPABILITY_MASK', 'hex'), ('NL80211_ATTR_MDID', 'hex'), ('NL80211_ATTR_IE_RIC', 'hex'), ('NL80211_ATTR_CRIT_PROT_ID', 'hex'), ('NL80211_ATTR_MAX_CRIT_PROT_DURATION', 'hex'), ('NL80211_ATTR_PEER_AID', 'hex'), ('NL80211_ATTR_COALESCE_RULE', 'hex'), ('NL80211_ATTR_CH_SWITCH_COUNT', 'hex'), ('NL80211_ATTR_CH_SWITCH_BLOCK_TX', 'hex'), ('NL80211_ATTR_CSA_IES', 'hex'), ('NL80211_ATTR_CSA_C_OFF_BEACON', 'hex'), ('NL80211_ATTR_CSA_C_OFF_PRESP', 'hex'), ('NL80211_ATTR_RXMGMT_FLAGS', 'hex'), ('NL80211_ATTR_STA_SUPPORTED_CHANNELS', 'hex'), ('NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES', 'hex'), ('NL80211_ATTR_HANDLE_DFS', 'hex'), ('NL80211_ATTR_SUPPORT_5_MHZ', 'hex'), ('NL80211_ATTR_SUPPORT_10_MHZ', 'hex'), ('NL80211_ATTR_OPMODE_NOTIF', 'hex'), ('NL80211_ATTR_VENDOR_ID', 'hex'), ('NL80211_ATTR_VENDOR_SUBCMD', 'hex'), ('NL80211_ATTR_VENDOR_DATA', 'hex'), ('NL80211_ATTR_VENDOR_EVENTS', 'hex'), ('NL80211_ATTR_QOS_MAP', 'hex'), ('NL80211_ATTR_MAC_HINT', 'hex'), ('NL80211_ATTR_WIPHY_FREQ_HINT', 'hex'), ('NL80211_ATTR_MAX_AP_ASSOC_STA', 'hex'), ('NL80211_ATTR_TDLS_PEER_CAPABILITY', 'hex'), ('NL80211_ATTR_SOCKET_OWNER', 'hex'), ('NL80211_ATTR_CSA_C_OFFSETS_TX', 'hex'), ('NL80211_ATTR_MAX_CSA_COUNTERS', 'hex'), ('NL80211_ATTR_TDLS_INITIATOR', 'hex'), ('NL80211_ATTR_USE_RRM', 'hex'), ('NL80211_ATTR_WIPHY_DYN_ACK', 'hex'), ('NL80211_ATTR_TSID', 'hex'), ('NL80211_ATTR_USER_PRIO', 'hex'), ('NL80211_ATTR_ADMITTED_TIME', 'hex'), ('NL80211_ATTR_SMPS_MODE', 'hex'), ('NL80211_ATTR_OPER_CLASS', 'hex'), ('NL80211_ATTR_MAC_MASK', 'hex'), ('NL80211_ATTR_WIPHY_SELF_MANAGED_REG', 'hex'), ('NUM_NL80211_ATTR', 'hex')) class bss(nla): class elementsBinary(nla_base): def binary_supported_rates(self, rawdata): # pdb.set_trace() string = "" for byteRaw in rawdata: (byte,) = struct.unpack("B", byteRaw) r = byte & 0x7f if r == BSS_MEMBERSHIP_SELECTOR_VHT_PHY and byte & 0x80: string += "VHT" elif r == BSS_MEMBERSHIP_SELECTOR_HT_PHY and byte & 0x80: string += "HT" else: string += "%d.%d" % (r / 2, 5 * (r & 1)) string += "%s " % ("*" if byte & 0x80 else "") return string def binary_vendor(self, rawdata): ''' Extract vendor data ''' vendor = {} # pdb.set_trace() size = len(rawdata) # if len > 4 and rawdata[0] == ms_oui[0] # and rawdata[1] == ms_oui[1] and rawdata[2] == ms_oui[2] if size < 3: vendor["VENDOR_NAME"] = "Vendor specific: kernel request/get-response TASKSTATS_CMD_NEW = 2 class tcmd(genlmsg): nla_map = (('TASKSTATS_CMD_ATTR_UNSPEC', 'none'), ('TASKSTATS_CMD_ATTR_PID', 'uint32'), ('TASKSTATS_CMD_ATTR_TGID', 'uint32'), ('TASKSTATS_CMD_ATTR_REGISTER_CPUMASK', 'asciiz'), ('TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK', 'asciiz')) class tstats(nla): pack = "struct" fields = (('version', 'H'), # 2 ('ac_exitcode', 'I'), # 4 ('ac_flag', 'B'), # 1 ('ac_nice', 'B'), # 1 --- 10 ('cpu_count', 'Q'), # 8 ('cpu_delay_total', 'Q'), # 8 ('blkio_count', 'Q'), # 8 ('blkio_delay_total', 'Q'), # 8 ('swapin_count', 'Q'), # 8 ('swapin_delay_total', 'Q'), # 8 ('cpu_run_real_total', 'Q'), # 8 ('cpu_run_virtual_total', 'Q'), # 8 ('ac_comm', '32s'), # 32 +++ 112 ('ac_sched', 'B'), # 1 ('__pad', '3x'), # 1 --- 8 (!) ('ac_uid', 'I'), # 4 +++ 120 ('ac_gid', 'I'), # 4 ('ac_pid', 'I'), # 4 ('ac_ppid', 'I'), # 4 ('ac_btime', 'I'), # 4 +++ 136 ('ac_etime', 'Q'), # 8 +++ 144 ('ac_utime', 'Q'), # 8 ('ac_stime', 'Q'), # 8 ('ac_minflt', 'Q'), # 8 ('ac_majflt', 'Q'), # 8 ('coremem', 'Q'), # 8 ('virtmem', 'Q'), # 8 ('hiwater_rss', 'Q'), # 8 ('hiwater_vm', 'Q'), # 8 ('read_char', 'Q'), # 8 ('write_char', 'Q'), # 8 ('read_syscalls', 'Q'), # 8 ('write_syscalls', 'Q'), # 8 ('read_bytes', 'Q'), # ... ('write_bytes', 'Q'), ('cancelled_write_bytes', 'Q'), ('nvcsw', 'Q'), ('nivcsw', 'Q'), ('ac_utimescaled', 'Q'), ('ac_stimescaled', 'Q'), ('cpu_scaled_run_real_total', 'Q')) def decode(self): nla.decode(self) self['ac_comm'] = self['ac_comm'][:self['ac_comm'].find('\0')] class taskstatsmsg(genlmsg): nla_map = (('TASKSTATS_TYPE_UNSPEC', 'none'), ('TASKSTATS_TYPE_PID', 'uint32'), ('TASKSTATS_TYPE_TGID', 'uint32'), ('TASKSTATS_TYPE_STATS', 'stats'), ('TASKSTATS_TYPE_AGGR_PID', 'aggr_pid'), ('TASKSTATS_TYPE_AGGR_TGID', 'aggr_tgid')) class stats(tstats): pass # FIXME: optimize me! class aggr_id(nla): nla_map = (('TASKSTATS_TYPE_UNSPEC', 'none'), ('TASKSTATS_TYPE_PID', 'uint32'), ('TASKSTATS_TYPE_TGID', 'uint32'), ('TASKSTATS_TYPE_STATS', 'stats')) class stats(tstats): pass class aggr_pid(aggr_id): pass class aggr_tgid(aggr_id): pass class TaskStats(GenericNetlinkSocket): def __init__(self): GenericNetlinkSocket.__init__(self) def bind(self): GenericNetlinkSocket.bind(self, 'TASKSTATS', taskstatsmsg) def get_pid_stat(self, pid): ''' Get taskstats for a process. Pid should be an integer. ''' msg = tcmd() msg['cmd'] = TASKSTATS_CMD_GET msg['version'] = 1 msg['attrs'].append(['TASKSTATS_CMD_ATTR_PID', pid]) return self.nlm_request(msg, self.prid, msg_flags=NLM_F_REQUEST) def _register_mask(self, cmd, mask): msg = tcmd() msg['cmd'] = TASKSTATS_CMD_GET msg['version'] = 1 msg['attrs'].append([cmd, mask]) # there is no response to this request self.put(msg, self.prid, msg_flags=NLM_F_REQUEST) def register_mask(self, mask): ''' Start the accounting for a processors by a mask. Mask is a string, e.g.:: 0,1 -- first two CPUs 0-4,6-10 -- CPUs from 0 to 4 and from 6 to 10 When the accounting is turned on, on can receive messages with get() routine. Though the kernel has a procedure, that cleans up accounting, when it is not used, it is recommended to run deregister_mask() before process exit. ''' self.monitor(True) self._register_mask('TASKSTATS_CMD_ATTR_REGISTER_CPUMASK', mask) def deregister_mask(self, mask): ''' Stop the accounting. ''' self.monitor(False) self._register_mask('TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK', mask)