pyroute2-0.4.21/0000775000175000017500000000000013152333366013312 5ustar peetpeet00000000000000pyroute2-0.4.21/LICENSE.Apache.v20000664000175000017500000002612413123443516016027 0ustar peetpeet00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2016 Peter V. Saveliev Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. pyroute2-0.4.21/LICENSE.GPL.v20000664000175000017500000004325413123443516015273 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.4.21/pyroute2/0000775000175000017500000000000013152333366015103 5ustar peetpeet00000000000000pyroute2-0.4.21/pyroute2/netlink/0000775000175000017500000000000013152333366016547 5ustar peetpeet00000000000000pyroute2-0.4.21/pyroute2/netlink/nfnetlink/0000775000175000017500000000000013152333366020537 5ustar peetpeet00000000000000pyroute2-0.4.21/pyroute2/netlink/nfnetlink/nftables.py0000664000175000017500000003334213123443516022711 0ustar peetpeet00000000000000from pyroute2.netlink import NLM_F_REQUEST from pyroute2.netlink import NLM_F_DUMP from pyroute2.netlink import NETLINK_NETFILTER from pyroute2.netlink import nla from pyroute2.netlink.nlsocket import NetlinkSocket from pyroute2.netlink.nfnetlink import nfgen_msg from pyroute2.netlink.nfnetlink import NFNL_SUBSYS_NFTABLES NFT_MSG_NEWTABLE = 0 NFT_MSG_GETTABLE = 1 NFT_MSG_DELTABLE = 2 NFT_MSG_NEWCHAIN = 3 NFT_MSG_GETCHAIN = 4 NFT_MSG_DELCHAIN = 5 NFT_MSG_NEWRULE = 6 NFT_MSG_GETRULE = 7 NFT_MSG_DELRULE = 8 NFT_MSG_NEWSET = 9 NFT_MSG_GETSET = 10 NFT_MSG_DELSET = 11 NFT_MSG_NEWSETELEM = 12 NFT_MSG_GETSETELEM = 13 NFT_MSG_DELSETELEM = 14 NFT_MSG_NEWGEN = 15 NFT_MSG_GETGEN = 16 NFT_MSG_TRACE = 17 class nft_gen_msg(nfgen_msg): nla_map = (('NFTA_GEN_UNSPEC', 'none'), ('NFTA_GEN_ID', 'be32')) class nft_chain_msg(nfgen_msg): nla_map = (('NFTA_CHAIN_UNSPEC', 'none'), ('NFTA_CHAIN_TABLE', 'asciiz'), ('NFTA_CHAIN_HANDLE', 'be64'), ('NFTA_CHAIN_NAME', 'asciiz'), ('NFTA_CHAIN_HOOK', 'hook'), ('NFTA_CHAIN_POLICY', 'be32'), ('NFTA_CHAIN_USE', 'be32'), ('NFTA_CHAIN_TYPE', 'asciiz'), ('NFTA_CHAIN_COUNTERS', 'counters')) class counters(nla): nla_map = (('NFTA_COUNTER_UNSPEC', 'none'), ('NFTA_COUNTER_BYTES', 'be64'), ('NFTA_COUNTER_PACKETS', 'be64')) class hook(nla): nla_map = (('NFTA_HOOK_UNSPEC', 'none'), ('NFTA_HOOK_HOOKNUM', 'be32'), ('NFTA_HOOK_PRIORITY', 'be32'), ('NFTA_HOOK_DEV', 'asciiz')) class nft_map_uint8(nla): ops = {} fields = [('value', 'B')] def decode(self): nla.decode(self) self.value = self.ops.get(self['value']) class nft_map_be32(nft_map_uint8): fields = [('value', '>I')] class nft_regs(nla): class regs(nft_map_be32): ops = {0x00: 'NFT_REG_VERDICT', 0x01: 'NFT_REG_1', 0x02: 'NFT_REG_2', 0x03: 'NFT_REG_3', 0x04: 'NFT_REG_4', 0x08: 'NFT_REG32_00', 0x09: 'MFT_REG32_01', 0x0a: 'NFT_REG32_02', 0x0b: 'NFT_REG32_03', 0x0c: 'NFT_REG32_04', 0x0d: 'NFT_REG32_05', 0x0e: 'NFT_REG32_06', 0x0f: 'NFT_REG32_07', 0x10: 'NFT_REG32_08', 0x11: 'NFT_REG32_09', 0x12: 'NFT_REG32_10', 0x13: 'NFT_REG32_11', 0x14: 'NFT_REG32_12', 0x15: 'NFT_REG32_13', 0x16: 'NFT_REG32_14', 0x17: 'NFT_REG32_15'} class nft_data(nla): class data(nla): nla_map = (('NFTA_DATA_UNSPEC', 'none'), ('NFTA_DATA_VALUE', 'cdata'), ('NFTA_DATA_VERDICT', 'verdict')) class verdict(nla): nla_map = (('NFTA_VERDICT_UNSPEC', 'none'), ('NFTA_VERDICT_CODE', 'be32'), ('NFTA_VERDICT_CHAIN', 'asciiz')) class nft_rule_msg(nfgen_msg): nla_map = (('NFTA_RULE_UNSPEC', 'none'), ('NFTA_RULE_TABLE', 'asciiz'), ('NFTA_RULE_CHAIN', 'asciiz'), ('NFTA_RULE_HANDLE', 'be64'), ('NFTA_RULE_EXPRESSIONS', '*rule_expr'), ('NFTA_RULE_COMPAT', 'hex'), ('NFTA_RULE_POSITION', 'be64'), ('NFTA_RULE_USERDATA', 'hex')) class rule_expr(nla): nla_map = (('NFTA_EXPR_UNSPEC', 'none'), ('NFTA_EXPR_NAME', 'asciiz'), ('NFTA_EXPR_DATA', 'expr')) class nft_bitwise(nft_data, nft_regs): nla_map = (('NFTA_BITWISE_UNSPEC', 'none'), ('NFTA_BITWISE_SREG', 'regs'), ('NFTA_BITWISE_DREG', 'regs'), ('NFTA_BITWISE_LEN', 'be32'), ('NFTA_BITWISE_MASK', 'data'), ('NFTA_BITWISE_XOR', 'data')) class nft_byteorder(nft_regs): nla_map = (('NFTA_BYTEORDER_UNSPEC', 'none'), ('NFTA_BYTEORDER_SREG', 'regs'), ('NFTA_BYTEORDER_DREG', 'regs'), ('NFTA_BYTEORDER_OP', 'ops'), ('NFTA_BYTEORDER_LEN', 'be32'), ('NFTA_BYTEORDER_SIZE', 'be32')) class ops(nft_map_be32): ops = {0: 'NFT_BYTEORDER_NTOH', 1: 'NFT_BYTEORDER_HTON'} class nft_cmp(nft_data, nft_regs): nla_map = (('NFTA_CMP_UNSPEC', 'none'), ('NFTA_CMP_SREG', 'regs'), ('NFTA_CMP_OP', 'ops'), ('NFTA_CMP_DATA', 'data')) class ops(nft_map_be32): ops = {0: 'NFT_CMP_EQ', 1: 'NFT_CMP_NEQ', 2: 'NFT_CMP_LT', 3: 'NFT_CMP_LTE', 4: 'NFT_CMP_GT', 5: 'NFT_CMP_GTE'} class nft_counter(nla): nla_map = (('NFTA_COUNTER_UNSPEC', 'none'), ('NFTA_COUNTER_BYTES', 'be64'), ('NFTA_COUNTER_PACKETS', 'be64')) class nft_ct(nft_regs): nla_map = (('NFTA_CT_UNSPEC', 'none'), ('NFTA_CT_DREG', 'regs'), ('NFTA_CT_KEY', 'keys'), ('NFTA_CT_DIRECTION', 'uint8'), ('NFTA_CT_SREG', 'regs')) class keys(nft_map_be32): ops = {0x00: 'NFT_CT_STATE', 0x01: 'NFT_CT_DIRECTION', 0x02: 'NFT_CT_STATUS', 0x03: 'NFT_CT_MARK', 0x04: 'NFT_CT_SECMARK', 0x05: 'NFT_CT_EXPIRATION', 0x06: 'NFT_CT_HELPER', 0x07: 'NFT_CT_L3PROTOCOL', 0x08: 'NFT_CT_SRC', 0x09: 'NFT_CT_DST', 0x0a: 'NFT_CT_PROTOCOL', 0x0b: 'NFT_CT_PROTO_SRC', 0x0c: 'NFT_CT_PROTO_DST', 0x0d: 'NFT_CT_LABELS', 0x0e: 'NFT_CT_PKTS', 0x0f: 'NFT_CT_BYTES'} class nft_exthdr(nft_regs): nla_map = (('NFTA_EXTHDR_UNSPEC', 'none'), ('NFTA_EXTHDR_DREG', 'regs'), ('NFTA_EXTHDR_TYPE', 'uint8'), ('NFTA_EXTHDR_OFFSET', 'be32'), ('NFTA_EXTHDR_LEN', 'be32')) class nft_immediate(nft_data, nft_regs): nla_map = (('NFTA_IMMEDIATE_UNSPEC', 'none'), ('NFTA_IMMEDIATE_DREG', 'regs'), ('NFTA_IMMEDIATE_DATA', 'data')) class nft_limit(nla): nla_map = (('NFTA_LIMIT_UNSPEC', 'none'), ('NFTA_LIMIT_RATE', 'be64'), ('NFTA_LIMIT_UNIT', 'be64'), ('NFTA_LIMIT_BURST', 'be32'), ('NFTA_LIMIT_TYPE', 'types'), ('NFTA_LIMIT_FLAGS', 'be32')) # make flags type class types(nft_map_be32): ops = {0: 'NFT_LIMIT_PKTS', 1: 'NFT_LIMIT_PKT_BYTES'} class nft_log(nla): nla_map = (('NFTA_LOG_UNSPEC', 'none'), ('NFTA_LOG_GROUP', 'be32'), ('NFTA_LOG_PREFIX', 'asciiz'), ('NFTA_LOG_SNAPLEN', 'be32'), ('NFTA_LOG_QTHRESHOLD', 'be32'), ('NFTA_LOG_LEVEL', 'be32'), ('NFTA_LOG_FLAGS', 'be32')) class nft_lookup(nft_regs): nla_map = (('NFTA_LOOKUP_UNSPEC', 'none'), ('NFTA_LOOKUP_SET', 'asciiz'), ('NFTA_LOOKUP_SREG', 'regs'), ('NFTA_LOOKUP_DREG', 'regs'), ('NFTA_LOOKUP_SET_ID', 'be32')) class nft_masq(nla): nla_map = (('NFTA_MASQ_UNSPEC', 'none'), ('NFTA_MASQ_FLAGS', 'be32')) class nft_meta(nft_regs): nla_map = (('NFTA_META_UNSPEC', 'none'), ('NFTA_META_DREG', 'regs'), ('NFTA_META_KEY', 'be32'), ('NFTA_META_SREG', 'regs')) class nft_nat(nft_regs): nla_map = (('NFTA_NAT_UNSPEC', 'none'), ('NFTA_NAT_TYPE', 'types'), ('NFTA_NAT_FAMILY', 'be32'), ('NFTA_NAT_REG_ADDR_MIN', 'regs'), ('NFTA_NAT_REG_ADDR_MAX', 'regs'), ('NFTA_NAT_REG_PROTO_MIN', 'regs'), ('NFTA_NAT_REG_PROTO_MAX', 'regs'), ('NFTA_NAT_FLAGS', 'be32')) class types(nft_map_be32): ops = {0: 'NFT_NAT_SNAT', 1: 'NFT_NAT_DNAT'} class nft_payload(nft_regs): nla_map = (('NFTA_PAYLOAD_UNSPEC', 'none'), ('NFTA_PAYLOAD_DREG', 'regs'), ('NFTA_PAYLOAD_BASE', 'base_type'), ('NFTA_PAYLOAD_OFFSET', 'be32'), ('NFTA_PAYLOAD_LEN', 'be32'), ('NFTA_PAYLOAD_SREG', 'regs'), ('NFTA_PAYLOAD_CSUM_TYPE', 'csum_type'), ('NFTA_PAYLOAD_CSUM_OFFSET', 'be32')) class base_type(nft_map_be32): ops = {0: 'NFT_PAYLOAD_LL_HEADER', 1: 'NFT_PAYLOAD_NETWORK_HEADER', 2: 'NFT_PAYLOAD_TRANSPORT_HEADER'} class csum_type(nft_map_be32): ops = {0: 'NFT_PAYLOAD_CSUM_NONE', 1: 'NFT_PAYLOAD_CSUM_INET'} # RFC 791 class nft_queue(nla): nla_map = (('NFTA_QUEUE_UNSPEC', 'none'), ('NFTA_QUEUE_NUM', 'be16'), ('NFTA_QUEUE_TOTAL', 'be16'), ('NFTA_QUEUE_FLAGS', 'be16')) class nft_redir(nft_regs): nla_map = (('NFTA_REDIR_UNSPEC', 'none'), ('NFTA_REDIR_REG_PROTO_MIN', 'regs'), ('NFTA_REDIR_REG_PROTO_MAX', 'regs'), ('NFTA_REDIR_FLAGS', 'be32')) class nft_reject(nla): nla_map = (('NFTA_REJECT_UNSPEC', 'none'), ('NFTA_REJECT_TYPE', 'types'), ('NFTA_REJECT_ICMP_CODE', 'codes')) class types(nft_map_be32): ops = {0: 'NFT_REJECT_ICMP_UNREACH', 1: 'NFT_REJECT_TCP_RST', 2: 'NFT_REJECT_ICMPX_UNREACH'} class codes(nft_map_uint8): ops = {0: 'NFT_REJECT_ICMPX_NO_ROUTE', 1: 'NFT_REJECT_ICMPX_PORT_UNREACH', 2: 'NFT_REJECT_ICMPX_HOST_UNREACH', 3: 'NFT_REJECT_ICMPX_ADMIN_PROHIBITED'} @staticmethod def expr(self, *argv, **kwarg): data_type = self.get_attr('NFTA_EXPR_NAME') return getattr(self, 'nft_%s' % data_type, self.hex) class nft_set_msg(nfgen_msg): nla_map = (('NFTA_SET_UNSPEC', 'none'), ('NFTA_SET_TABLE', 'asciiz'), ('NFTA_SET_NAME', 'asciiz'), ('NFTA_SET_FLAGS', 'be32'), ('NFTA_SET_KEY_TYPE', 'be32'), ('NFTA_SET_KEY_LEN', 'be32'), ('NFTA_SET_DATA_TYPE', 'be32'), ('NFTA_SET_DATA_LEN', 'be32'), ('NFTA_SET_POLICY', 'be32'), ('NFTA_SET_DESC', 'hex'), ('NFTA_SET_ID', 'be32'), ('NFTA_SET_TIMEOUT', 'be32'), ('NFTA_SET_GC_INTERVAL', 'be32'), ('NFTA_SET_USERDATA', 'hex')) class nft_table_msg(nfgen_msg): nla_map = (('NFTA_TABLE_UNSPEC', 'none'), ('NFTA_TABLE_NAME', 'asciiz'), ('NFTA_TABLE_FLAGS', 'be32'), ('NFTA_TABLE_USE', 'be32')) class NFTSocket(NetlinkSocket): ''' NFNetlink socket (family=NETLINK_NETFILTER). Implements API to the nftables functionality. ''' policy = {NFT_MSG_NEWTABLE: nft_table_msg, NFT_MSG_GETTABLE: nft_table_msg, NFT_MSG_DELTABLE: nft_table_msg, NFT_MSG_NEWCHAIN: nft_chain_msg, NFT_MSG_GETCHAIN: nft_chain_msg, NFT_MSG_DELCHAIN: nft_chain_msg, NFT_MSG_NEWRULE: nft_rule_msg, NFT_MSG_GETRULE: nft_rule_msg, NFT_MSG_DELRULE: nft_rule_msg, NFT_MSG_NEWSET: nft_set_msg, NFT_MSG_GETSET: nft_set_msg, NFT_MSG_DELSET: nft_set_msg, NFT_MSG_NEWGEN: nft_gen_msg, NFT_MSG_GETGEN: nft_gen_msg} def __init__(self, version=1, attr_revision=0, nfgen_family=2): super(NFTSocket, self).__init__(family=NETLINK_NETFILTER) policy = dict([(x | (NFNL_SUBSYS_NFTABLES << 8), y) for (x, y) in self.policy.items()]) self.register_policy(policy) self._proto_version = version self._attr_revision = attr_revision self._nfgen_family = nfgen_family def request(self, msg, msg_type, msg_flags=NLM_F_REQUEST | NLM_F_DUMP, terminate=None): msg['nfgen_family'] = self._nfgen_family return self.nlm_request(msg, msg_type | (NFNL_SUBSYS_NFTABLES << 8), msg_flags, terminate=terminate) def get_tables(self): return self.request(nfgen_msg(), NFT_MSG_GETTABLE) def get_chains(self): return self.request(nfgen_msg(), NFT_MSG_GETCHAIN) def get_rules(self): return self.request(nfgen_msg(), NFT_MSG_GETRULE) def get_sets(self): return self.request(nfgen_msg(), NFT_MSG_GETSET) pyroute2-0.4.21/pyroute2/netlink/nfnetlink/__init__.py0000664000175000017500000000134513123443516022650 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. ''' from pyroute2.netlink import nlmsg 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_ACCT = 7 NFNL_SUBSYS_CTNETLINK_TIMEOUT = 8 NFNL_SUBSYS_CTHELPER = 9 NFNL_SUBSYS_NFTABLES = 10 NFNL_SUBSYS_NFT_COMPAT = 11 NFNL_SUBSYS_COUNT = 12 class nfgen_msg(nlmsg): fields = (('nfgen_family', 'B'), ('version', 'B'), ('res_id', 'H')) pyroute2-0.4.21/pyroute2/netlink/nfnetlink/ipset.py0000664000175000017500000001511113127143031022222 0ustar peetpeet00000000000000from pyroute2.netlink import nla from pyroute2.netlink import NLA_F_NESTED from pyroute2.netlink import NLA_F_NET_BYTEORDER from pyroute2.netlink.nfnetlink import nfgen_msg from pyroute2.netlink.nfnetlink import NFNL_SUBSYS_IPSET IPSET_MAXNAMELEN = 32 IPSET_DEFAULT_MAXELEM = 65536 IPSET_CMD_NONE = 0 IPSET_CMD_PROTOCOL = 1 # Return protocol version IPSET_CMD_CREATE = 2 # Create a new (empty) set IPSET_CMD_DESTROY = 3 # Destroy a (empty) set IPSET_CMD_FLUSH = 4 # Remove all elements from a set IPSET_CMD_RENAME = 5 # Rename a set IPSET_CMD_SWAP = 6 # Swap two sets IPSET_CMD_LIST = 7 # List sets IPSET_CMD_SAVE = 8 # Save sets IPSET_CMD_ADD = 9 # Add an element to a set IPSET_CMD_DEL = 10 # Delete an element from a set IPSET_CMD_TEST = 11 # Test an element in a set IPSET_CMD_HEADER = 12 # Get set header data only IPSET_CMD_TYPE = 13 # 13: Get set type IPSET_FLAG_WITH_COUNTERS = 1 << 3 IPSET_FLAG_WITH_COMMENT = 1 << 4 IPSET_FLAG_WITH_FORCEADD = 1 << 5 IPSET_FLAG_WITH_SKBINFO = 1 << 6 IPSET_ERR_PROTOCOL = 4097 IPSET_ERR_FIND_TYPE = 4098 IPSET_ERR_MAX_SETS = 4099 IPSET_ERR_BUSY = 4100 IPSET_ERR_EXIST_SETNAME2 = 4101 IPSET_ERR_TYPE_MISMATCH = 4102 IPSET_ERR_EXIST = 4103 IPSET_ERR_INVALID_CIDR = 4104 IPSET_ERR_INVALID_NETMASK = 4105 IPSET_ERR_INVALID_FAMILY = 4106 IPSET_ERR_TIMEOUT = 4107 IPSET_ERR_REFERENCED = 4108 IPSET_ERR_IPADDR_IPV4 = 4109 IPSET_ERR_IPADDR_IPV6 = 4110 IPSET_ERR_COUNTER = 4111 IPSET_ERR_COMMENT = 4112 IPSET_ERR_INVALID_MARKMASK = 4113 IPSET_ERR_SKBINFO = 4114 class ipset_base(nla): class ipset_ip(nla): nla_flags = NLA_F_NESTED nla_map = (('IPSET_ATTR_UNSPEC', 'none'), ('IPSET_ATTR_IPADDR_IPV4', 'ip4addr', NLA_F_NET_BYTEORDER), ('IPSET_ATTR_IPADDR_IPV6', 'ip6addr', NLA_F_NET_BYTEORDER)) class ipset_msg(nfgen_msg): ''' Since the support just begins to be developed, many attrs are still in `hex` format -- just to dump the content. ''' nla_map = (('IPSET_ATTR_UNSPEC', 'none'), ('IPSET_ATTR_PROTOCOL', 'uint8'), ('IPSET_ATTR_SETNAME', 'asciiz'), ('IPSET_ATTR_TYPENAME', 'asciiz'), ('IPSET_ATTR_REVISION', 'uint8'), ('IPSET_ATTR_FAMILY', 'uint8'), ('IPSET_ATTR_FLAGS', 'hex'), ('IPSET_ATTR_DATA', 'get_data_type'), ('IPSET_ATTR_ADT', 'attr_adt'), ('IPSET_ATTR_LINENO', 'hex'), ('IPSET_ATTR_PROTOCOL_MIN', 'uint8')) @staticmethod def get_data_type(self, *args, **kwargs): # create and list commands have specific attributes. See linux_ip_set.h # for more information (and/or lib/PROTOCOL of ipset sources) cmd = self['header']['type'] & ~(NFNL_SUBSYS_IPSET << 8) if cmd == IPSET_CMD_CREATE or cmd == IPSET_CMD_LIST: return self.cadt_data return self.ipset_generic.adt_data class ipset_generic(ipset_base): class adt_data(ipset_base): nla_flags = NLA_F_NESTED nla_map = ((0, 'IPSET_ATTR_UNSPEC', 'none'), (1, 'IPSET_ATTR_IP', 'ipset_ip'), (1, 'IPSET_ATTR_IP_FROM', 'ipset_ip'), (2, 'IPSET_ATTR_IP_TO', 'ipset_ip'), (3, 'IPSET_ATTR_CIDR', 'be8', NLA_F_NET_BYTEORDER), (4, 'IPSET_ATTR_PORT', 'be16', NLA_F_NET_BYTEORDER), (4, 'IPSET_ATTR_PORT_FROM', 'be16', NLA_F_NET_BYTEORDER), (5, 'IPSET_ATTR_PORT_TO', 'be16', NLA_F_NET_BYTEORDER), (6, 'IPSET_ATTR_TIMEOUT', 'be32', NLA_F_NET_BYTEORDER), (7, 'IPSET_ATTR_PROTO', 'recursive'), (8, 'IPSET_ATTR_CADT_FLAGS', 'be32', NLA_F_NET_BYTEORDER), (9, 'IPSET_ATTR_CADT_LINENO', 'be32'), (10, 'IPSET_ATTR_MARK', 'be32', NLA_F_NET_BYTEORDER), (11, 'IPSET_ATTR_MARKMASK', 'be32', NLA_F_NET_BYTEORDER), (17, 'IPSET_ATTR_ETHER', 'l2addr'), (18, 'IPSET_ATTR_NAME', 'asciiz'), (19, 'IPSET_ATTR_NAMEREF', 'be32'), (20, 'IPSET_ATTR_IP2', 'be32'), (21, 'IPSET_ATTR_CIDR2', 'hex'), (22, 'IPSET_ATTR_IP2_TO', 'hex'), (23, 'IPSET_ATTR_IFACE', 'asciiz'), (24, 'IPSET_ATTR_BYTES', 'be64', NLA_F_NET_BYTEORDER), (25, 'IPSET_ATTR_PACKETS', 'be64', NLA_F_NET_BYTEORDER), (26, 'IPSET_ATTR_COMMENT', 'asciiz'), (27, 'IPSET_ATTR_SKBMARK', 'hex'), (28, 'IPSET_ATTR_SKBPRIO', 'be32'), (29, 'IPSET_ATTR_SKBQUEUE', 'hex')) class cadt_data(ipset_base): nla_flags = NLA_F_NESTED nla_map = ((0, 'IPSET_ATTR_UNSPEC', 'none'), (1, 'IPSET_ATTR_IP', 'ipset_ip'), (1, 'IPSET_ATTR_IP_FROM', 'ipset_ip'), (2, 'IPSET_ATTR_IP_TO', 'ipset_ip'), (3, 'IPSET_ATTR_CIDR', 'be8', NLA_F_NET_BYTEORDER), (4, 'IPSET_ATTR_PORT', 'be16', NLA_F_NET_BYTEORDER), (4, 'IPSET_ATTR_PORT_FROM', 'be16', NLA_F_NET_BYTEORDER), (5, 'IPSET_ATTR_PORT_TO', 'be16', NLA_F_NET_BYTEORDER), (6, 'IPSET_ATTR_TIMEOUT', 'be32', NLA_F_NET_BYTEORDER), (7, 'IPSET_ATTR_PROTO', 'recursive'), (8, 'IPSET_ATTR_CADT_FLAGS', 'be32', NLA_F_NET_BYTEORDER), (9, 'IPSET_ATTR_CADT_LINENO', 'be32'), (10, 'IPSET_ATTR_MARK', 'be32', NLA_F_NET_BYTEORDER), (11, 'IPSET_ATTR_MARKMASK', 'be32', NLA_F_NET_BYTEORDER), (17, 'IPSET_ATTR_GC', 'hex'), (18, 'IPSET_ATTR_HASHSIZE', 'be32', NLA_F_NET_BYTEORDER), (19, 'IPSET_ATTR_MAXELEM', 'be32', NLA_F_NET_BYTEORDER), (20, 'IPSET_ATTR_NETMASK', 'hex'), (21, 'IPSET_ATTR_PROBES', 'hex'), (22, 'IPSET_ATTR_RESIZE', 'hex'), (23, 'IPSET_ATTR_SIZE', 'be32', NLA_F_NET_BYTEORDER), (24, 'IPSET_ATTR_ELEMENTS', 'be32', NLA_F_NET_BYTEORDER), (25, 'IPSET_ATTR_REFERENCES', 'be32', NLA_F_NET_BYTEORDER), (26, 'IPSET_ATTR_MEMSIZE', 'be32', NLA_F_NET_BYTEORDER)) class attr_adt(ipset_generic): nla_flags = NLA_F_NESTED nla_map = ((7, 'IPSET_ATTR_DATA', 'adt_data'),) pyroute2-0.4.21/pyroute2/netlink/nl80211/0000775000175000017500000000000013152333366017554 5ustar peetpeet00000000000000pyroute2-0.4.21/pyroute2/netlink/nl80211/__init__.py0000664000175000017500000007070613123443516021674 0ustar peetpeet00000000000000''' NL80211 module ============== TODO ''' import struct 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 # 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_TIM = 5 NL80211_BSS_ELEMENTS_EXTENDED_RATE = 50 NL80211_BSS_ELEMENTS_VENDOR = 221 BSS_MEMBERSHIP_SELECTOR_HT_PHY = 127 BSS_MEMBERSHIP_SELECTOR_VHT_PHY = 126 # interface types NL80211_IFTYPE_UNSPECIFIED = 0 NL80211_IFTYPE_ADHOC = 1 NL80211_IFTYPE_STATION = 2 NL80211_IFTYPE_AP = 3 NL80211_IFTYPE_AP_VLAN = 4 NL80211_IFTYPE_WDS = 5 NL80211_IFTYPE_MONITOR = 6 NL80211_IFTYPE_MESH_POINT = 7 NL80211_IFTYPE_P2P_CLIENT = 8 NL80211_IFTYPE_P2P_GO = 9 NL80211_IFTYPE_P2P_DEVICE = 10 NL80211_IFTYPE_OCB = 11 (IFTYPE_NAMES, IFTYPE_VALUES) = map_namespace('NL80211_IFTYPE_', globals(), normalize=True) # channel width NL80211_CHAN_WIDTH_20_NOHT = 0 # 20 MHz non-HT channel NL80211_CHAN_WIDTH_20 = 1 # 20 MHz HT channel NL80211_CHAN_WIDTH_40 = 2 # 40 MHz HT channel NL80211_CHAN_WIDTH_80 = 3 # 80 MHz channel NL80211_CHAN_WIDTH_80P80 = 4 # 80+80 MHz channel NL80211_CHAN_WIDTH_160 = 5 # 160 MHz channel NL80211_CHAN_WIDTH_5 = 6 # 5 MHz OFDM channel NL80211_CHAN_WIDTH_10 = 7 # 10 MHz OFDM channel (CHAN_WIDTH, WIDTH_VALUES) = map_namespace('NL80211_CHAN_WIDTH_', globals(), normalize=True) # BSS "status" NL80211_BSS_STATUS_AUTHENTICATED = 0 # Authenticated with this BS NL80211_BSS_STATUS_ASSOCIATED = 1 # Associated with this BSS NL80211_BSS_STATUS_IBSS_JOINED = 2 # Joined to this IBSS (BSS_STATUS_NAMES, BSS_STATUS_VALUES) = map_namespace('NL80211_BSS_STATUS_', globals(), normalize=True) class nl80211cmd(genlmsg): prefix = 'NL80211_ATTR_' nla_map = (('NL80211_ATTR_UNSPEC', 'none'), ('NL80211_ATTR_WIPHY', 'uint32'), ('NL80211_ATTR_WIPHY_NAME', 'asciiz'), ('NL80211_ATTR_IFINDEX', 'uint32'), ('NL80211_ATTR_IFNAME', 'asciiz'), ('NL80211_ATTR_IFTYPE', 'uint32'), ('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', 'uint32'), ('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', 'uint8'), ('NL80211_ATTR_SCAN_FREQUENCIES', 'hex'), ('NL80211_ATTR_SCAN_SSIDS', 'hex'), ('NL80211_ATTR_GENERATION', 'uint32'), ('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', 'string'), ('NL80211_ATTR_AUTH_TYPE', 'hex'), ('NL80211_ATTR_REASON_CODE', 'hex'), ('NL80211_ATTR_KEY_TYPE', 'hex'), ('NL80211_ATTR_MAX_SCAN_IE_LEN', 'uint16'), ('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', 'uint8'), ('NL80211_ATTR_WIPHY_RETRY_LONG', 'uint8'), ('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', 'uint8'), ('NL80211_ATTR_DURATION', 'hex'), ('NL80211_ATTR_COOKIE', 'hex'), ('NL80211_ATTR_WIPHY_COVERAGE_CLASS', 'uint8'), ('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', 'uint32'), ('NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX', 'uint32'), ('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', 'uint8'), ('NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN', 'uint16'), ('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', 'uint8'), ('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', 'uint32'), ('NL80211_ATTR_CENTER_FREQ2', 'uint32'), ('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', 'array(uint8)'), ('NL80211_ATTR_EXT_CAPA_MASK', 'array(uint8)'), ('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', 'array(uint8)'), ('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_rates(self, offset, length): init = offset string = "" while (offset - init) < length: byte, = struct.unpack_from('B', self.data, offset) 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)) offset += 1 string += "%s " % ("*" if byte & 0x80 else "") return string def binary_tim(self, offset): (count, period, bitmapc, bitmap0) = struct.unpack_from('BBBB', self.data, offset) return ("DTIM Count {0} DTIM Period {1} Bitmap Control 0x{2} " "Bitmap[0] 0x{3}".format(count, period, bitmapc, bitmap0)) def decode_nlas(self): return def decode(self): nla_base.decode(self) self.value = {} init = offset = self.offset + 4 while (offset-init) < (self.length-4): (msg_type, length) = struct.unpack_from('BB', self.data, offset) if msg_type == NL80211_BSS_ELEMENTS_SSID: self.value["SSID"], = (struct .unpack_from('%is' % length, self.data, offset + 2)) if msg_type == NL80211_BSS_ELEMENTS_SUPPORTED_RATES: supported_rates = self.binary_rates(offset + 2, length) self.value["SUPPORTED_RATES"] = supported_rates if msg_type == NL80211_BSS_ELEMENTS_CHANNEL: channel, = struct.unpack_from('B', self.data, offset + 2) self.value["CHANNEL"] = channel if msg_type == NL80211_BSS_ELEMENTS_TIM: self.value["TRAFFIC INDICATION MAP"] = \ self.binary_tim(offset + 2) if msg_type == NL80211_BSS_ELEMENTS_EXTENDED_RATE: extended_rates = self.binary_rates(offset + 2, length) self.value["EXTENDED_RATES"] = extended_rates if msg_type == NL80211_BSS_ELEMENTS_VENDOR: self.value["VENDOR"], = (struct .unpack_from('%is' % length, self.data, offset + 2)) offset += length + 2 prefix = 'NL80211_BSS_' nla_map = (('__NL80211_BSS_INVALID', 'hex'), ('NL80211_BSS_BSSID', 'hex'), ('NL80211_BSS_FREQUENCY', 'uint32'), ('NL80211_BSS_TSF', 'uint64'), ('NL80211_BSS_BEACON_INTERVAL', 'uint16'), ('NL80211_BSS_CAPABILITY', 'uint16'), ('NL80211_BSS_INFORMATION_ELEMENTS', 'elementsBinary'), ('NL80211_BSS_SIGNAL_MBM', 'int32'), ('NL80211_BSS_SIGNAL_UNSPEC', 'uint8'), ('NL80211_BSS_STATUS', 'uint32'), ('NL80211_BSS_SEEN_MS_AGO', 'uint32'), ('NL80211_BSS_BEACON_IES', 'hex'), ('NL80211_BSS_CHAN_WIDTH', 'uint32'), ('NL80211_BSS_BEACON_TSF', 'uint64'), ('NL80211_BSS_PRESP_DATA', 'hex'), ('NL80211_BSS_MAX', 'hex') ) class MarshalNl80211(Marshal): msg_map = {NL80211_CMD_UNSPEC: nl80211cmd, NL80211_CMD_GET_WIPHY: nl80211cmd, NL80211_CMD_SET_WIPHY: nl80211cmd, NL80211_CMD_NEW_WIPHY: nl80211cmd, NL80211_CMD_DEL_WIPHY: nl80211cmd, NL80211_CMD_GET_INTERFACE: nl80211cmd, NL80211_CMD_SET_INTERFACE: nl80211cmd, NL80211_CMD_NEW_INTERFACE: nl80211cmd, NL80211_CMD_DEL_INTERFACE: nl80211cmd, NL80211_CMD_GET_KEY: nl80211cmd, NL80211_CMD_SET_KEY: nl80211cmd, NL80211_CMD_NEW_KEY: nl80211cmd, NL80211_CMD_DEL_KEY: nl80211cmd, NL80211_CMD_GET_BEACON: nl80211cmd, NL80211_CMD_SET_BEACON: nl80211cmd, NL80211_CMD_START_AP: nl80211cmd, NL80211_CMD_NEW_BEACON: nl80211cmd, NL80211_CMD_STOP_AP: nl80211cmd, NL80211_CMD_DEL_BEACON: nl80211cmd, NL80211_CMD_GET_STATION: nl80211cmd, NL80211_CMD_SET_STATION: nl80211cmd, NL80211_CMD_NEW_STATION: nl80211cmd, NL80211_CMD_DEL_STATION: nl80211cmd, NL80211_CMD_GET_MPATH: nl80211cmd, NL80211_CMD_SET_MPATH: nl80211cmd, NL80211_CMD_NEW_MPATH: nl80211cmd, NL80211_CMD_DEL_MPATH: nl80211cmd, NL80211_CMD_SET_BSS: nl80211cmd, NL80211_CMD_SET_REG: nl80211cmd, NL80211_CMD_REQ_SET_REG: nl80211cmd, NL80211_CMD_GET_MESH_CONFIG: nl80211cmd, NL80211_CMD_SET_MESH_CONFIG: nl80211cmd, NL80211_CMD_SET_MGMT_EXTRA_IE: nl80211cmd, NL80211_CMD_GET_REG: nl80211cmd, NL80211_CMD_GET_SCAN: nl80211cmd, NL80211_CMD_TRIGGER_SCAN: nl80211cmd, NL80211_CMD_NEW_SCAN_RESULTS: nl80211cmd, NL80211_CMD_SCAN_ABORTED: nl80211cmd, NL80211_CMD_REG_CHANGE: nl80211cmd, NL80211_CMD_AUTHENTICATE: nl80211cmd, NL80211_CMD_ASSOCIATE: nl80211cmd, NL80211_CMD_DEAUTHENTICATE: nl80211cmd, NL80211_CMD_DISASSOCIATE: nl80211cmd, NL80211_CMD_MICHAEL_MIC_FAILURE: nl80211cmd, NL80211_CMD_REG_BEACON_HINT: nl80211cmd, NL80211_CMD_JOIN_IBSS: nl80211cmd, NL80211_CMD_LEAVE_IBSS: nl80211cmd, NL80211_CMD_TESTMODE: nl80211cmd, NL80211_CMD_CONNECT: nl80211cmd, NL80211_CMD_ROAM: nl80211cmd, NL80211_CMD_DISCONNECT: nl80211cmd, NL80211_CMD_SET_WIPHY_NETNS: nl80211cmd, NL80211_CMD_GET_SURVEY: nl80211cmd, NL80211_CMD_NEW_SURVEY_RESULTS: nl80211cmd, NL80211_CMD_SET_PMKSA: nl80211cmd, NL80211_CMD_DEL_PMKSA: nl80211cmd, NL80211_CMD_FLUSH_PMKSA: nl80211cmd, NL80211_CMD_REMAIN_ON_CHANNEL: nl80211cmd, NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL: nl80211cmd, NL80211_CMD_SET_TX_BITRATE_MASK: nl80211cmd, NL80211_CMD_REGISTER_FRAME: nl80211cmd, NL80211_CMD_REGISTER_ACTION: nl80211cmd, NL80211_CMD_FRAME: nl80211cmd, NL80211_CMD_ACTION: nl80211cmd, NL80211_CMD_FRAME_TX_STATUS: nl80211cmd, NL80211_CMD_ACTION_TX_STATUS: nl80211cmd, NL80211_CMD_SET_POWER_SAVE: nl80211cmd, NL80211_CMD_GET_POWER_SAVE: nl80211cmd, NL80211_CMD_SET_CQM: nl80211cmd, NL80211_CMD_NOTIFY_CQM: nl80211cmd, NL80211_CMD_SET_CHANNEL: nl80211cmd, NL80211_CMD_SET_WDS_PEER: nl80211cmd, NL80211_CMD_FRAME_WAIT_CANCEL: nl80211cmd, NL80211_CMD_JOIN_MESH: nl80211cmd, NL80211_CMD_LEAVE_MESH: nl80211cmd, NL80211_CMD_UNPROT_DEAUTHENTICATE: nl80211cmd, NL80211_CMD_UNPROT_DISASSOCIATE: nl80211cmd, NL80211_CMD_NEW_PEER_CANDIDATE: nl80211cmd, NL80211_CMD_GET_WOWLAN: nl80211cmd, NL80211_CMD_SET_WOWLAN: nl80211cmd, NL80211_CMD_START_SCHED_SCAN: nl80211cmd, NL80211_CMD_STOP_SCHED_SCAN: nl80211cmd, NL80211_CMD_SCHED_SCAN_RESULTS: nl80211cmd, NL80211_CMD_SCHED_SCAN_STOPPED: nl80211cmd, NL80211_CMD_SET_REKEY_OFFLOAD: nl80211cmd, NL80211_CMD_PMKSA_CANDIDATE: nl80211cmd, NL80211_CMD_TDLS_OPER: nl80211cmd, NL80211_CMD_TDLS_MGMT: nl80211cmd, NL80211_CMD_UNEXPECTED_FRAME: nl80211cmd, NL80211_CMD_PROBE_CLIENT: nl80211cmd, NL80211_CMD_REGISTER_BEACONS: nl80211cmd, NL80211_CMD_UNEXPECTED_4ADDR_FRAME: nl80211cmd, NL80211_CMD_SET_NOACK_MAP: nl80211cmd, NL80211_CMD_CH_SWITCH_NOTIFY: nl80211cmd, NL80211_CMD_START_P2P_DEVICE: nl80211cmd, NL80211_CMD_STOP_P2P_DEVICE: nl80211cmd, NL80211_CMD_CONN_FAILED: nl80211cmd, NL80211_CMD_SET_MCAST_RATE: nl80211cmd, NL80211_CMD_SET_MAC_ACL: nl80211cmd, NL80211_CMD_RADAR_DETECT: nl80211cmd, NL80211_CMD_GET_PROTOCOL_FEATURES: nl80211cmd, NL80211_CMD_UPDATE_FT_IES: nl80211cmd, NL80211_CMD_FT_EVENT: nl80211cmd, NL80211_CMD_CRIT_PROTOCOL_START: nl80211cmd, NL80211_CMD_CRIT_PROTOCOL_STOP: nl80211cmd, NL80211_CMD_GET_COALESCE: nl80211cmd, NL80211_CMD_SET_COALESCE: nl80211cmd, NL80211_CMD_CHANNEL_SWITCH: nl80211cmd, NL80211_CMD_VENDOR: nl80211cmd, NL80211_CMD_SET_QOS_MAP: nl80211cmd, NL80211_CMD_ADD_TX_TS: nl80211cmd, NL80211_CMD_DEL_TX_TS: nl80211cmd, NL80211_CMD_GET_MPP: nl80211cmd, NL80211_CMD_JOIN_OCB: nl80211cmd, NL80211_CMD_LEAVE_OCB: nl80211cmd, NL80211_CMD_CH_SWITCH_STARTED_NOTIFY: nl80211cmd, NL80211_CMD_TDLS_CHANNEL_SWITCH: nl80211cmd, NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH: nl80211cmd, NL80211_CMD_WIPHY_REG_CHANGE: nl80211cmd} def fix_message(self, msg): try: msg['event'] = NL80211_VALUES[msg['cmd']] except Exception: pass class NL80211(GenericNetlinkSocket): def __init__(self): GenericNetlinkSocket.__init__(self) self.marshal = MarshalNl80211() def bind(self, groups=0, async=False): GenericNetlinkSocket.bind(self, 'nl80211', nl80211cmd, groups, None, async) pyroute2-0.4.21/pyroute2/netlink/taskstats/0000775000175000017500000000000013152333366020570 5ustar peetpeet00000000000000pyroute2-0.4.21/pyroute2/netlink/taskstats/__init__.py0000664000175000017500000001365513123443516022710 0ustar peetpeet00000000000000''' TaskStats module ================ All that you should know about TaskStats, is that you should not use it. But if you have to, ok:: import os from pyroute2 import TaskStats ts = TaskStats() ts.get_pid_stat(os.getpid()) It is not implemented normally yet, but some methods are already usable. ''' from pyroute2.netlink import NLM_F_REQUEST from pyroute2.netlink import nla from pyroute2.netlink import genlmsg from pyroute2.netlink.generic import GenericNetlinkSocket TASKSTATS_CMD_UNSPEC = 0 # Reserved TASKSTATS_CMD_GET = 1 # user->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 ('__ac_pad', '3x'), # 3 # (the ac_uid field is aligned(8), so we add more padding) ('__implicit_pad', '4x'), # 4 ('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 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._register_mask('TASKSTATS_CMD_ATTR_REGISTER_CPUMASK', mask) def deregister_mask(self, mask): ''' Stop the accounting. ''' self._register_mask('TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK', mask) pyroute2-0.4.21/pyroute2/netlink/generic/0000775000175000017500000000000013152333366020163 5ustar peetpeet00000000000000pyroute2-0.4.21/pyroute2/netlink/generic/__init__.py0000664000175000017500000000517513127143031022272 0ustar peetpeet00000000000000# -*- coding: utf-8 -*- ''' Generic netlink =============== Describe ''' import errno import logging 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 SOL_NETLINK from pyroute2.netlink import NETLINK_ADD_MEMBERSHIP from pyroute2.netlink import NETLINK_DROP_MEMBERSHIP 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. ''' mcast_groups = {} def bind(self, proto, msg_class, groups=0, pid=None, 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 msg = self.discovery(proto) self.prid = msg.get_attr('CTRL_ATTR_FAMILY_ID') self.mcast_groups = \ dict([(x.get_attr('CTRL_ATTR_MCAST_GRP_NAME'), x.get_attr('CTRL_ATTR_MCAST_GRP_ID')) for x in msg.get_attr('CTRL_ATTR_MCAST_GROUPS', [])]) self.marshal.msg_map[self.prid] = msg_class def add_membership(self, group): self.setsockopt(SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, self.mcast_groups[group]) def drop_membership(self, group): self.setsockopt(SOL_NETLINK, NETLINK_DROP_MEMBERSHIP, self.mcast_groups[group]) def discovery(self, proto): ''' Resolve generic netlink protocol -- takes a string as the only parameter, return protocol description ''' 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.data, (0, 0)) msg = self.get()[0] err = msg['header'].get('error', None) if err is not None: if hasattr(err, 'code') and err.code == errno.ENOENT: logging.error('Generic netlink protocol %s not found' % proto) logging.error('Please check if the protocol module is loaded') raise err return msg pyroute2-0.4.21/pyroute2/netlink/rtnl/0000775000175000017500000000000013152333366017526 5ustar peetpeet00000000000000pyroute2-0.4.21/pyroute2/netlink/rtnl/rtmsg.py0000664000175000017500000001467213127143031021234 0ustar peetpeet00000000000000import struct from socket import inet_ntop from socket import inet_pton from socket import AF_UNSPEC from socket import AF_INET from socket import AF_INET6 from pyroute2.common import AF_MPLS from pyroute2.common import hexdump from pyroute2.common import map_namespace from pyroute2.netlink import nlmsg from pyroute2.netlink import nla RTNH_F_DEAD = 1 RTNH_F_PERVASIVE = 2 RTNH_F_ONLINK = 4 RTNH_F_OFFLOAD = 8 RTNH_F_LINKDOWN = 16 (RTNH_F_NAMES, RTNH_F_VALUES) = map_namespace('RTNH_F', globals()) class nlflags(object): def encode(self): if isinstance(self['flags'], (set, tuple, list)): self['flags'] = self.names2flags(self['flags']) return super(nlflags, self).encode() def flags2names(self, flags=None): ret = [] for flag in RTNH_F_VALUES: if (flag & flags) == flag: ret.append(RTNH_F_VALUES[flag].lower()[7:]) return ret def names2flags(self, flags=None): ret = 0 for flag in flags or self['flags']: ret |= RTNH_F_NAMES['RTNH_F_' + flag.upper()] return ret class rtmsg_base(nlflags): ''' Route message C structure:: 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; }; ''' __slots__ = () 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', 'target'), ('RTA_SRC', 'target'), ('RTA_IIF', 'uint32'), ('RTA_OIF', 'uint32'), ('RTA_GATEWAY', 'target'), ('RTA_PRIORITY', 'uint32'), ('RTA_PREFSRC', 'target'), ('RTA_METRICS', 'metrics'), ('RTA_MULTIPATH', '*get_nh'), ('RTA_PROTOINFO', 'uint32'), ('RTA_FLOW', 'uint32'), ('RTA_CACHEINFO', 'cacheinfo'), ('RTA_SESSION', 'hex'), ('RTA_MP_ALGO', 'hex'), ('RTA_TABLE', 'uint32'), ('RTA_MARK', 'uint32'), ('RTA_MFC_STATS', 'rta_mfc_stats'), ('RTA_VIA', 'rtvia'), ('RTA_NEWDST', 'target'), ('RTA_PREF', 'hex'), ('RTA_ENCAP_TYPE', 'uint16'), ('RTA_ENCAP', 'encap_info'), ('RTA_EXPIRES', 'hex')) @staticmethod def encap_info(self, *argv, **kwarg): return self.mpls_encap_info class mpls_encap_info(nla): __slots__ = () nla_map = (('MPLS_IPTUNNEL_UNSPEC', 'none'), ('MPLS_IPTUNNEL_DST', 'mpls_target')) class rta_mfc_stats(nla): __slots__ = () fields = (('mfcs_packets', 'uint64'), ('mfcs_bytes', 'uint64'), ('mfcs_wrong_if', 'uint64')) class metrics(nla): __slots__ = () 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')) @staticmethod def get_nh(self, *argv, **kwarg): return nh class rtvia(nla): __slots__ = () fields = (('value', 's'), ) def encode(self): family = self.get('family', AF_UNSPEC) if family in (AF_INET, AF_INET6): addr = inet_pton(family, self['addr']) else: raise TypeError('Family %s not supported for RTA_VIA' % family) self['value'] = struct.pack('H', family) + addr nla.encode(self) def decode(self): nla.decode(self) family = struct.unpack('H', self['value'][:2])[0] addr = self['value'][2:] if len(addr): if (family == AF_INET and len(addr) == 4) or \ (family == AF_INET6 and len(addr) == 16): addr = inet_ntop(family, addr) else: addr = hexdump(addr) self.value = {'family': family, 'addr': addr} class cacheinfo(nla): __slots__ = () 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')) class rtmsg(rtmsg_base, nlmsg): __slots__ = () def encode(self): if self.get('family') == AF_MPLS: # force fields self['dst_len'] = 20 self['table'] = 254 self['type'] = 1 # assert NLA types for n in self.get('attrs', []): if n[0] not in ('RTA_OIF', 'RTA_DST', 'RTA_VIA', 'RTA_NEWDST', 'RTA_MULTIPATH'): raise TypeError('Incorrect NLA type %s for AF_MPLS' % n[0]) super(rtmsg_base, self).encode() class nh(rtmsg_base, nla): __slots__ = () is_nla = False cell_header = (('length', 'H'), ) fields = (('flags', 'B'), ('hops', 'B'), ('oif', 'i')) pyroute2-0.4.21/pyroute2/netlink/rtnl/tcmsg/0000775000175000017500000000000013152333366020643 5ustar peetpeet00000000000000pyroute2-0.4.21/pyroute2/netlink/rtnl/tcmsg/sched_netem.py0000664000175000017500000001007013123443516023466 0ustar peetpeet00000000000000from pyroute2.netlink import nla from pyroute2.netlink.rtnl import TC_H_ROOT from pyroute2.netlink.rtnl.tcmsg.common import time2tick from pyroute2.netlink.rtnl.tcmsg.common import percent2u32 parent = TC_H_ROOT def get_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 options(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')) pyroute2-0.4.21/pyroute2/netlink/rtnl/tcmsg/sched_drr.py0000664000175000017500000000140213123443516023144 0ustar peetpeet00000000000000''' drr +++ The qdisc doesn't accept any parameters, but the class accepts `quantum` parameter:: ip.tc('add', 'drr', interface, '1:') ip.tc('add-class', 'drr', interface, '1:10', quantum=1600) ip.tc('add-class', 'drr', interface, '1:20', quantum=1600) ''' from pyroute2.netlink import nla from pyroute2.netlink.rtnl import TC_H_ROOT from pyroute2.netlink.rtnl.tcmsg.common import stats2 as c_stats2 parent = TC_H_ROOT def get_class_parameters(kwarg): return {'attrs': [['TCA_DRR_QUANTUM', kwarg.get('quantum', 0)]]} class options(nla): nla_map = (('TCA_DRR_UNSPEC', 'none'), ('TCA_DRR_QUANTUM', 'uint32')) class stats(nla): fields = (('deficit', 'I'), ) class stats2(c_stats2): class stats_app(stats): pass pyroute2-0.4.21/pyroute2/netlink/rtnl/tcmsg/sched_bpf.py0000664000175000017500000000513613123443516023134 0ustar peetpeet00000000000000''' ''' from socket import htons from pyroute2.netlink import nla from pyroute2.netlink.rtnl import TC_H_ROOT from pyroute2.netlink import NLA_F_NESTED from pyroute2.protocols import ETH_P_ALL from pyroute2.netlink.rtnl.tcmsg.common import stats2 from pyroute2.netlink.rtnl.tcmsg.common import TCA_ACT_MAX_PRIO from pyroute2.netlink.rtnl.tcmsg.common_act import get_tca_action from pyroute2.netlink.rtnl.tcmsg.common_act import nla_plus_tca_act_opt from pyroute2.netlink.rtnl.tcmsg.act_police import nla_plus_police from pyroute2.netlink.rtnl.tcmsg.act_police import get_parameters \ as ap_parameters parent = TC_H_ROOT TCA_BPF_FLAG_ACT_DIRECT = 1 def fix_msg(msg, kwarg): msg['info'] = htons(kwarg.pop('protocol', ETH_P_ALL) & 0xffff) |\ ((kwarg.pop('prio', 0) << 16) & 0xffff0000) def get_parameters(kwarg): ret = {'attrs': []} attrs_map = ( # ('action', 'TCA_BPF_ACT'), # ('police', 'TCA_BPF_POLICE'), ('classid', 'TCA_BPF_CLASSID'), ('fd', 'TCA_BPF_FD'), ('name', 'TCA_BPF_NAME'), ('flags', 'TCA_BPF_FLAGS'), ) act = kwarg.get('action') if act: ret['attrs'].append(['TCA_BPF_ACT', get_tca_action(kwarg)]) if kwarg.get('rate'): ret['attrs'].append(['TCA_BPF_POLICE', ap_parameters(kwarg)]) kwarg['flags'] = kwarg.get('flags', 0) if kwarg.get('direct_action', False): kwarg['flags'] |= TCA_BPF_FLAG_ACT_DIRECT for k, v in attrs_map: r = kwarg.get(k, None) if r is not None: ret['attrs'].append([v, r]) return ret class options(nla, nla_plus_police): nla_map = (('TCA_BPF_UNSPEC', 'none'), ('TCA_BPF_ACT', 'bpf_act'), ('TCA_BPF_POLICE', 'police'), ('TCA_BPF_CLASSID', 'uint32'), ('TCA_BPF_OPS_LEN', 'uint32'), ('TCA_BPF_OPS', 'uint32'), ('TCA_BPF_FD', 'uint32'), ('TCA_BPF_NAME', 'asciiz'), ('TCA_BPF_FLAGS', 'uint32')) class bpf_act(nla): nla_flags = NLA_F_NESTED nla_map = tuple([('TCA_ACT_PRIO_%i' % x, 'tca_act_bpf') for x in range(TCA_ACT_MAX_PRIO)]) class tca_act_bpf(nla, nla_plus_tca_act_opt): nla_map = (('TCA_ACT_UNSPEC', 'none'), ('TCA_ACT_KIND', 'asciiz'), ('TCA_ACT_OPTIONS', 'get_act_options'), ('TCA_ACT_INDEX', 'hex'), ('TCA_ACT_STATS', 'get_stats2')) @staticmethod def get_stats2(self, *argv, **kwarg): return stats2 pyroute2-0.4.21/pyroute2/netlink/rtnl/tcmsg/sched_tbf.py0000664000175000017500000000227113123443516023135 0ustar peetpeet00000000000000from pyroute2.netlink.rtnl import TC_H_ROOT from pyroute2.netlink.rtnl.tcmsg.common import get_rate_parameters from pyroute2.netlink.rtnl.tcmsg.common import nla_plus_rtab parent = TC_H_ROOT def get_parameters(kwarg): parms = get_rate_parameters(kwarg) # fill parameters return {'attrs': [['TCA_TBF_PARMS', parms], ['TCA_TBF_RTAB', True]]} class options(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')) pyroute2-0.4.21/pyroute2/netlink/rtnl/tcmsg/act_mirred.py0000664000175000017500000000343313127143031023317 0ustar peetpeet00000000000000from pyroute2.netlink import nla from pyroute2.netlink import NLA_F_NESTED from pyroute2.netlink.rtnl.tcmsg.common import tc_actions """ Mirred - mirror/redirect action see tc-mirred(8) Use like any other action, with the following parameters available: - direction (mandatory): ingress or egress - action (mandatory): mirror or redirect - ifindex (mandatory): destination interface for mirrored or redirected packets - index: explicit index for this action """ # see tc_mirred.h MIRRED_EACTIONS = { ("egress", "redirect"): 1, # redirect packet to egress ("egress", "mirror"): 2, # mirror packet to egress ("ingress", "redirect"): 3, # redirect packet to ingress ("ingress", "mirror"): 4, # mirror packet to ingress } class options(nla): nla_flags = NLA_F_NESTED nla_map = (('TCA_MIRRED_UNSPEC', 'none'), ('TCA_MIRRED_TM', 'none'), ('TCA_MIRRED_PARMS', 'tca_mirred_parms'), ) class tca_mirred_parms(nla): fields = (('index', 'I'), ('capab', 'I'), ('action', 'i'), ('refcnt', 'i'), ('bindcnt', 'i'), ('eaction', 'i'), ('ifindex', 'I'), ) def get_parameters(kwarg): ret = {'attrs': []} # direction, action and ifindex are mandatory parms = { 'eaction': MIRRED_EACTIONS[(kwarg['direction'], kwarg['action'])], 'ifindex': kwarg['ifindex'], } if 'index' in kwarg: parms['index'] = int(kwarg['index']) # From m_mirred.c if kwarg['action'] == 'redirect': parms['action'] = tc_actions['stolen'] else: # mirror parms['action'] = tc_actions['pipe'] ret['attrs'].append(['TCA_MIRRED_PARMS', parms]) return ret pyroute2-0.4.21/pyroute2/netlink/rtnl/tcmsg/sched_hfsc.py0000664000175000017500000000450313123443516023305 0ustar peetpeet00000000000000''' hfsc ++++ Simple HFSC example:: eth0 = ip.get_links(ifname="eth0")[0] ip.tc("add", "hfsc", eth0, handle="1:", default="1:1") ip.tc("add-class", "hfsc", eth0, handle="1:1", parent="1:0" rsc={"m2": "5mbit"}) HFSC curve nla types: * `rsc`: real-time curve * `fsc`: link-share curve * `usc`: upper-limit curve ''' from pyroute2.netlink.rtnl.tcmsg.common import stats2 as c_stats2 from pyroute2.netlink.rtnl.tcmsg.common import get_rate from pyroute2.netlink.rtnl.tcmsg.common import get_time from pyroute2.netlink import nla from pyroute2.netlink.rtnl import RTM_NEWQDISC from pyroute2.netlink.rtnl import RTM_DELQDISC from pyroute2.netlink.rtnl import TC_H_ROOT parent = TC_H_ROOT def get_parameters(kwarg): defcls = kwarg.get('default', kwarg.get('defcls', 0x10)) defcls &= 0xffff return {'defcls': defcls} def get_class_parameters(kwarg): ret = {'attrs': []} for key in ('rsc', 'fsc', 'usc'): if key in kwarg: ret['attrs'].append(['TCA_HFSC_%s' % key.upper(), {'m1': get_rate(kwarg[key].get('m1', 0)), 'd': get_time(kwarg[key].get('d', 0)), 'm2':get_rate(kwarg[key].get('m2', 0))}]) return ret class options_hfsc(nla): fields = (('defcls', 'H'),) # default class class options_hfsc_class(nla): nla_map = (('TCA_HFSC_UNSPEC', 'none'), ('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_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 def options(msg, *argv, **kwarg): if msg['header']['type'] in (RTM_NEWQDISC, RTM_DELQDISC): return options_hfsc else: return options_hfsc_class class stats2(c_stats2): class stats_app(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 pyroute2-0.4.21/pyroute2/netlink/rtnl/tcmsg/act_connmark.py0000664000175000017500000000243613127143031023647 0ustar peetpeet00000000000000from pyroute2.netlink import nla from pyroute2.netlink import NLA_F_NESTED from pyroute2.netlink.rtnl.tcmsg.common import tc_actions """ connmark - netfilter connmark retriever action see tc-connmark(8) This filter restores the connection mark into the packet mark. Connection marks are typically handled by the CONNMARK iptables module. See iptables-extensions(8). There is no mandatory parameter, but you can specify the action, which defaults to 'pipe', and the conntrack zone (see the manual). """ class options(nla): nla_flags = NLA_F_NESTED nla_map = (('TCA_CONNMARK_UNSPEC', 'none'), ('TCA_CONNMARK_PARMS', 'tca_connmark_parms'), ('TCA_CONNMARK_TM', 'none'), ) class tca_connmark_parms(nla): fields = (('index', 'I'), ('capab', 'I'), ('action', 'i'), ('refcnt', 'i'), ('bindcnt', 'i'), ('zone', 'H'), ('__padding', 'H'), # XXX is there a better way to do this ? ) def get_parameters(kwarg): ret = {'attrs': []} parms = { 'action': tc_actions[kwarg.get('action', 'pipe')], 'zone': kwarg.get('zone', 0) } ret['attrs'].append(['TCA_CONNMARK_PARMS', parms]) return ret pyroute2-0.4.21/pyroute2/netlink/rtnl/tcmsg/act_vlan.py0000664000175000017500000000276513152305337023013 0ustar peetpeet00000000000000from pyroute2.netlink import nla from pyroute2.netlink.rtnl.tcmsg.common import tc_actions from socket import htons v_actions = {'pop': 1, 'push': 2, 'modify': 3} class options(nla): nla_map = (('TCA_VLAN_UNSPEC', 'none'), ('TCA_VLAN_TM', 'none'), ('TCA_VLAN_PARMS', 'tca_vlan_parms'), ('TCA_VLAN_PUSH_VLAN_ID', 'uint16'), ('TCA_VLAN_PUSH_VLAN_PROTOCOL', 'uint16'), ('TCA_VLAN_PAD', 'none'), ('TCA_VLAN_PUSH_VLAN_PRIORITY', 'uint8')) class tca_vlan_parms(nla): fields = (('index', 'I'), ('capab', 'I'), ('action', 'i'), ('refcnt', 'i'), ('bindcnt', 'i'), ('v_action', 'i'),) def get_parameters(kwarg): ret = {'attrs': []} parms = {'v_action': v_actions[kwarg['v_action']]} parms['action'] = tc_actions[kwarg.get('action', 'pipe')] ret['attrs'].append(['TCA_VLAN_PARMS', parms]) # Vlan id compulsory for "push" and "modify" if kwarg['v_action'] in ['push', 'modify']: ret['attrs'].append(['TCA_VLAN_PUSH_VLAN_ID', kwarg['id']]) if 'priority' in kwarg: ret['attrs'].append(['TCA_VLAN_PUSH_VLAN_PRIORITY', kwarg['priority']]) if kwarg.get('protocol', '802.1Q') == '802.1ad': ret['attrs'].append(['TCA_VLAN_PUSH_VLAN_PROTOCOL', htons(0x88a8)]) else: ret['attrs'].append(['TCA_VLAN_PUSH_VLAN_PROTOCOL', htons(0x8100)]) return ret pyroute2-0.4.21/pyroute2/netlink/rtnl/tcmsg/sched_sfq.py0000664000175000017500000000520213123443516023150 0ustar peetpeet00000000000000from pyroute2.netlink.rtnl.tcmsg.common import get_size from pyroute2.netlink.rtnl.tcmsg.common import red_eval_ewma from pyroute2.netlink.rtnl.tcmsg.common import red_eval_P from pyroute2.netlink import nla from pyroute2.netlink.rtnl import TC_H_ROOT parent = TC_H_ROOT TC_RED_ECN = 1 TC_RED_HARDDROP = 2 TC_RED_ADAPTATIVE = 4 def get_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 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')) def options(*argv, **kwarg): if kwarg.get('length', 0) >= options_sfq_v1.get_size(): return options_sfq_v1 else: return options_sfq_v0 pyroute2-0.4.21/pyroute2/netlink/rtnl/tcmsg/sched_ingress.py0000664000175000017500000000033313123443516024031 0ustar peetpeet00000000000000from pyroute2.netlink import nla from pyroute2.netlink.rtnl import TC_H_INGRESS parent = TC_H_INGRESS def fix_msg(msg, kwarg): msg['handle'] = 0xffff0000 class options(nla): fields = (('value', 'I'), ) pyroute2-0.4.21/pyroute2/netlink/rtnl/tcmsg/sched_choke.py0000664000175000017500000000721613123443516023457 0ustar peetpeet00000000000000''' choke +++++ Parameters: * `limit` (required) -- int * `bandwith` (required) -- str/int * `min` -- int * `max` -- int * `avpkt` -- str/int, packet size * `burst` -- int * `probability` -- float * `ecn` -- bool Example:: ip.tc('add', 'choke', interface, limit=5500, bandwith="10mbit", ecn=True) ''' import struct import logging from pyroute2.netlink import nla from pyroute2.netlink.rtnl import TC_H_ROOT from pyroute2.netlink.rtnl.tcmsg.common import get_rate from pyroute2.netlink.rtnl.tcmsg.common import get_size from pyroute2.netlink.rtnl.tcmsg.common import red_eval_ewma from pyroute2.netlink.rtnl.tcmsg.common import red_eval_P from pyroute2.netlink.rtnl.tcmsg.common import red_eval_idle_damping from pyroute2.netlink.rtnl.tcmsg.common import stats2 as c_stats2 log = logging.getLogger(__name__) parent = TC_H_ROOT def get_parameters(kwarg): # The code is ported from iproute2 avpkt = 1000 probability = 0.02 opt = {'limit': kwarg['limit'], # required 'qth_min': kwarg.get('min', 0), 'qth_max': kwarg.get('max', 0), 'Wlog': 0, 'Plog': 0, 'Scell_log': 0, 'flags': 1 if kwarg.get('ecn') else 0} rate = get_rate(kwarg['bandwith']) # required burst = kwarg.get('burst', 0) avpkt = get_size(kwarg.get('avpkt', 1000)) probability = kwarg.get('probability', 0.02) if not opt['qth_max']: opt['qth_max'] = opt['limit'] // 4 if not opt['qth_min']: opt['qth_min'] = opt['qth_max'] // 3 if not burst: burst = (2 * opt['qth_min'] + opt['qth_max']) // 3 if opt['qth_max'] > opt['limit']: raise Exception('max is larger than limit') if opt['qth_min'] >= opt['qth_max']: raise Exception('min is not smaller than max') # Wlog opt['Wlog'] = red_eval_ewma(opt['qth_min']*avpkt, burst, avpkt) if opt['Wlog'] < 0: raise Exception('failed to calculate EWMA') elif opt['Wlog'] > 10: log.warning('choke: burst %s seems to be too large' % burst) # Plog opt['Plog'] = red_eval_P(opt['qth_min']*avpkt, opt['qth_max']*avpkt, probability) if opt['Plog'] < 0: raise Exception('choke: failed to calculate probability') # Scell_log, stab opt['Scell_log'], stab = red_eval_idle_damping(opt['Wlog'], avpkt, rate) if opt['Scell_log'] < 0: raise Exception('choke: failed to calculate idle damping table') return {'attrs': [['TCA_CHOKE_PARMS', opt], ['TCA_CHOKE_STAB', stab], ['TCA_CHOKE_MAX_P', int(probability * pow(2, 32))]]} class options(nla): nla_map = (('TCA_CHOKE_UNSPEC', 'none'), ('TCA_CHOKE_PARMS', 'qopt'), ('TCA_CHOKE_STAB', 'stab'), ('TCA_CHOKE_MAX_P', 'uint32')) class qopt(nla): fields = (('limit', 'I'), ('qth_min', 'I'), ('qth_max', 'I'), ('Wlog', 'B'), ('Plog', 'B'), ('Scell_log', 'B'), ('flags', 'B')) class stab(nla): fields = (('value', 's'), ) def encode(self): self['value'] = struct.pack('B' * 256, *(int(x) for x in self.value)) nla.encode(self) class stats(nla): fields = (('early', 'I'), ('pdrop', 'I'), ('other', 'I'), ('marked', 'I'), ('matched', 'I')) class stats2(c_stats2): class stats_app(stats): pass pyroute2-0.4.21/pyroute2/netlink/rtnl/tcmsg/cls_u32.py0000664000175000017500000002132413127143031022457 0ustar peetpeet00000000000000''' u32 +++ Filters can take an `action` argument, which affects the packet behavior when the filter matches. Currently the gact, bpf, and police action types are supported, and can be attached to the u32 and bpf filter types:: # An action can be a simple string, which translates to a gact type action = "drop" # Or it can be an explicit type (these are equivalent) action = dict(kind="gact", action="drop") # There can also be a chain of actions, which depend on the return # value of the previous action. action = [ dict(kind="bpf", fd=fd, name=name, action="ok"), dict(kind="police", rate="10kbit", burst=10240, limit=0), dict(kind="gact", action="ok"), ] # Add the action to a u32 match-all filter ip.tc("add", "htb", eth0, 0x10000, default=0x200000) ip.tc("add-filter", "u32", eth0, parent=0x10000, prio=10, protocol=protocols.ETH_P_ALL, target=0x10020, keys=["0x0/0x0+0"], action=action) # Add two more filters: One to send packets with a src address of # 192.168.0.1/32 into 1:10 and the second to send packets with a # dst address of 192.168.0.0/24 into 1:20 ip.tc("add-filter", "u32", eth0, parent=0x10000, prio=10, protocol=socket.AF_INET, target=0x10010, keys=["0xc0a80001/0xffffffff+12"]) # 0xc0a800010 = 192.168.0.1 # 0xffffffff = 255.255.255.255 (/32) # 12 = Source network field bit offset ip.tc("add-filter", "u32", eth0, parent=0x10000, prio=10, protocol=socket.AF_INET, target=0x10020, keys=["0xc0a80000/0xffffff00+16"]) # 0xc0a80000 = 192.168.0.0 # 0xffffff00 = 255.255.255.0 (/24) # 16 = Destination network field bit offset ''' import struct from socket import htons from pyroute2.netlink import nla from pyroute2.netlink import nlmsg from pyroute2.netlink.rtnl.tcmsg.common import stats2 from pyroute2.netlink.rtnl.tcmsg.common import TCA_ACT_MAX_PRIO from pyroute2.netlink.rtnl.tcmsg.common_act import get_tca_action from pyroute2.netlink.rtnl.tcmsg.common_act import nla_plus_tca_act_opt from pyroute2.netlink.rtnl.tcmsg.act_police import nla_plus_police from pyroute2.netlink.rtnl.tcmsg.act_police import get_parameters \ as ap_parameters def fix_msg(msg, kwarg): msg['info'] = htons(kwarg.get('protocol', 0) & 0xffff) |\ ((kwarg.get('prio', 0) << 16) & 0xffff0000) def get_parameters(kwarg): ret = {'attrs': []} if kwarg.get('rate'): ret['attrs'].append(['TCA_U32_POLICE', ap_parameters(kwarg)]) elif kwarg.get('action'): ret['attrs'].append(['TCA_U32_ACT', get_tca_action(kwarg)]) ret['attrs'].append(['TCA_U32_CLASSID', kwarg['target']]) ret['attrs'].append(['TCA_U32_SEL', {'keys': kwarg['keys']}]) return ret class options(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_tca_act_opt): nla_map = (('TCA_ACT_UNSPEC', 'none'), ('TCA_ACT_KIND', 'asciiz'), ('TCA_ACT_OPTIONS', 'get_act_options'), ('TCA_ACT_INDEX', 'hex'), ('TCA_ACT_STATS', 'stats2')) stats2 = stats2 class u32_sel(nla): fields = (('flags', 'B'), ('offshift', 'B'), ('nkeys', 'B'), ('__align', 'x'), ('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): ''' Key sample:: '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(data=self.data) 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(data=self.data) 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 nla.encode(self) offset = self.offset + 20 # 4 bytes header + 16 bytes fields for key in keys: key.offset = offset key.encode() offset += 16 # keys haven't header self.length = offset - self.offset struct.pack_into('H', self.data, self.offset, offset - self.offset) def decode(self): nla.decode(self) offset = self.offset + 16 self['keys'] = [] nkeys = self['nkeys'] while nkeys: key = self.u32_key(data=self.data, offset=offset) key.decode() offset += 16 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')) pyroute2-0.4.21/pyroute2/netlink/rtnl/tcmsg/__init__.py0000664000175000017500000000644013152305337022755 0ustar peetpeet00000000000000import types from pyroute2.netlink import nlmsg from pyroute2.netlink import nla from pyroute2.netlink.rtnl.tcmsg import cls_fw from pyroute2.netlink.rtnl.tcmsg import cls_u32 from pyroute2.netlink.rtnl.tcmsg import sched_bpf from pyroute2.netlink.rtnl.tcmsg import sched_choke from pyroute2.netlink.rtnl.tcmsg import sched_clsact from pyroute2.netlink.rtnl.tcmsg import sched_codel from pyroute2.netlink.rtnl.tcmsg import sched_drr from pyroute2.netlink.rtnl.tcmsg import sched_fq_codel from pyroute2.netlink.rtnl.tcmsg import sched_hfsc from pyroute2.netlink.rtnl.tcmsg import sched_htb from pyroute2.netlink.rtnl.tcmsg import sched_ingress from pyroute2.netlink.rtnl.tcmsg import sched_netem from pyroute2.netlink.rtnl.tcmsg import sched_pfifo_fast from pyroute2.netlink.rtnl.tcmsg import sched_plug from pyroute2.netlink.rtnl.tcmsg import sched_sfq from pyroute2.netlink.rtnl.tcmsg import sched_tbf from pyroute2.netlink.rtnl.tcmsg import sched_template plugins = {'plug': sched_plug, 'sfq': sched_sfq, 'clsact': sched_clsact, 'codel': sched_codel, 'fq_codel': sched_fq_codel, 'hfsc': sched_hfsc, 'htb': sched_htb, 'bpf': sched_bpf, 'tbf': sched_tbf, 'netem': sched_netem, 'fw': cls_fw, 'u32': cls_u32, 'ingress': sched_ingress, 'pfifo_fast': sched_pfifo_fast, 'choke': sched_choke, 'drr': sched_drr, 'prio': sched_pfifo_fast} class tcmsg(nlmsg): 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_plugin(self, plug, *argv, **kwarg): # get the plugin name kind = self.get_attr('TCA_KIND') # get the plugin implementation or the default one p = plugins.get(kind, sched_template) # get the interface interface = getattr(p, plug, getattr(sched_template, plug)) # if it is a method, run and return the result if isinstance(interface, types.FunctionType): return interface(self, *argv, **kwarg) else: return interface @staticmethod def get_stats2(self, *argv, **kwarg): return self.get_plugin('stats2', *argv, **kwarg) @staticmethod def get_xstats(self, *argv, **kwarg): return self.get_plugin('stats', *argv, **kwarg) @staticmethod def get_options(self, *argv, **kwarg): return self.get_plugin('options', *argv, **kwarg) pyroute2-0.4.21/pyroute2/netlink/rtnl/tcmsg/sched_codel.py0000664000175000017500000000320413123443516023445 0ustar peetpeet00000000000000import logging from pyroute2.netlink import nla from pyroute2.netlink.rtnl import TC_H_ROOT from pyroute2.netlink.rtnl.tcmsg.common import get_time from pyroute2.netlink.rtnl.tcmsg.common import stats2 as c_stats2 log = logging.getLogger(__name__) parent = TC_H_ROOT def get_parameters(kwarg): # # ACHTUNG: experimental code # # Parameters naming scheme WILL be changed in next releases # ret = {'attrs': []} transform = {'cdl_limit': lambda x: x, 'cdl_ecn': lambda x: x, 'cdl_target': get_time, 'cdl_ce_threshold': get_time, 'cdl_interval': get_time} for key in transform.keys(): if key in kwarg: log.warning('codel parameters naming will be changed ' 'in next releases (%s)' % key) ret['attrs'].append(['TCA_CODEL_%s' % key[4:].upper(), transform[key](kwarg[key])]) return ret class options(nla): nla_map = (('TCA_CODEL_UNSPEC', 'none'), ('TCA_CODEL_TARGET', 'uint32'), ('TCA_CODEL_LIMIT', 'uint32'), ('TCA_CODEL_INTERVAL', 'uint32'), ('TCA_CODEL_ECN', 'uint32'), ('TCA_CODEL_CE_THRESHOLD', 'uint32')) class stats(nla): fields = (('maxpacket', 'I'), ('count', 'I'), ('lastcount', 'I'), ('ldelay', 'I'), ('drop_next', 'I'), ('drop_overlimit', 'I'), ('ecn_mark', 'I'), ('dropping', 'I'), ('ce_mark', 'I')) class stats2(c_stats2): class stats_app(stats): pass pyroute2-0.4.21/pyroute2/netlink/rtnl/tcmsg/sched_fq_codel.py0000664000175000017500000000503513123443516024137 0ustar peetpeet00000000000000import logging import struct from pyroute2.netlink import nla from pyroute2.netlink.rtnl import TC_H_ROOT from pyroute2.netlink.rtnl.tcmsg import common from pyroute2.netlink.rtnl.tcmsg.common import get_time log = logging.getLogger(__name__) parent = TC_H_ROOT def get_parameters(kwarg): # # ACHTUNG: experimental code # # Parameters naming scheme WILL be changed in next releases # ret = {'attrs': []} transform = {'fqc_limit': lambda x: x, 'fqc_flows': lambda x: x, 'fqc_quantum': lambda x: x, 'fqc_ecn': lambda x: x, 'fqc_target': get_time, 'fqc_ce_threshold': get_time, 'fqc_interval': get_time} for key in transform.keys(): if key in kwarg: log.warning('fq_codel parameters naming will be changed ' 'in next releases (%s)' % key) ret['attrs'].append(['TCA_FQ_CODEL_%s' % key[4:].upper(), transform[key](kwarg[key])]) return ret class options(nla): nla_map = (('TCA_FQ_CODEL_UNSPEC', 'none'), ('TCA_FQ_CODEL_TARGET', 'uint32'), ('TCA_FQ_CODEL_LIMIT', 'uint32'), ('TCA_FQ_CODEL_INTERVAL', 'uint32'), ('TCA_FQ_CODEL_ECN', 'uint32'), ('TCA_FQ_CODEL_FLOWS', 'uint32'), ('TCA_FQ_CODEL_QUANTUM', 'uint32'), ('TCA_FQ_CODEL_CE_THRESHOLD', 'uint32')) class stats(nla): TCA_FQ_CODEL_XSTATS_QDISC = 0 TCA_FQ_CODEL_XSTATS_CLASS = 1 qdisc_fields = (('maxpacket', 'I'), ('drop_overlimit', 'I'), ('ecn_mark', 'I'), ('new_flow_count', 'I'), ('new_flows_len', 'I'), ('old_flows_len', 'I'), ('ce_mark', 'I')) class_fields = (('deficit', 'i'), ('ldelay', 'I'), ('count', 'I'), ('lastcount', 'I'), ('dropping', 'I'), ('drop_next', 'i')) def decode(self): nla.decode(self) # read the type kind = struct.unpack('I', self.buf.read(4))[0] if kind == self.TCA_FQ_CODEL_XSTATS_QDISC: self.fields = self.qdisc_fields elif kind == self.TCA_FQ_CODEL_XSTATS_CLASS: self.fields = self.class_fields else: raise TypeError("Unknown xstats type") self.decode_fields() class stats2(common.stats2): class stats_app(stats): pass pyroute2-0.4.21/pyroute2/netlink/rtnl/tcmsg/sched_template.py0000664000175000017500000000233613123443516024177 0ustar peetpeet00000000000000''' Template sched file. All the tcmsg plugins should be registered in `__init__.py`, see the `plugins` dict. All the methods, variables and classes are optional, but the naming scheme is fixed. ''' from pyroute2.netlink.rtnl.tcmsg import common from pyroute2.netlink import nla from pyroute2.netlink.rtnl import TC_H_ROOT # if you define the `parent` variable, it will be used # as the default parent value if no other value is # provided in the call options parent = TC_H_ROOT def fix_msg(kwarg, msg): ''' This method it called for all types -- classes, qdiscs and filters. Can be used to fix some `msg` fields. ''' pass def get_parameters(kwarg): ''' Called for qdiscs and filters. Should return the structure to be embedded as the qdisc parameters (`TCA_OPTIONS`). ''' return None def get_class_parameters(kwarg): ''' The same as above, but called only for classes. ''' return None class options(nla.hex): ''' The `TCA_OPTIONS` struct, by default not decoded. ''' pass class stats(nla.hex): ''' The struct to decode `TCA_XSTATS`. ''' pass class stats2(common.stats2): ''' The struct to decode `TCA_STATS2`. ''' pass pyroute2-0.4.21/pyroute2/netlink/rtnl/tcmsg/act_gact.py0000664000175000017500000000135013123443516022756 0ustar peetpeet00000000000000from pyroute2.netlink import nla from pyroute2.netlink import NLA_F_NESTED from pyroute2.netlink.rtnl.tcmsg.common import tc_actions class options(nla): nla_flags = NLA_F_NESTED nla_map = (('TCA_GACT_UNSPEC', 'none'), ('TCA_GACT_TM', 'none'), ('TCA_GACT_PARMS', 'tca_gact_parms'), ('TCA_GACT_PROB', 'none')) class tca_gact_parms(nla): fields = (('index', 'I'), ('capab', 'I'), ('action', 'i'), ('refcnt', 'i'), ('bindcnt', 'i')) def get_parameters(kwarg): ret = {'attrs': []} a = tc_actions[kwarg.get('action', 'drop')] ret['attrs'].append(['TCA_GACT_PARMS', {'action': a}]) return ret pyroute2-0.4.21/pyroute2/netlink/rtnl/tcmsg/common.py0000664000175000017500000002002013127143031022465 0ustar peetpeet00000000000000import re import os import struct import logging from math import log as logfm 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 nla log = logging.getLogger(__name__) LINKLAYER_UNSPEC = 0 LINKLAYER_ETHERNET = 1 LINKLAYER_ATM = 2 ATM_CELL_SIZE = 53 ATM_CELL_PAYLOAD = 48 TCA_ACT_MAX_PRIO = 32 TIME_UNITS_PER_SEC = 1000000 try: with open('/proc/net/psched', 'r') as psched: [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 except IOError as e: log.warning("tcmsg: %s", e) log.warning("the tc subsystem functionality is limited") clock_res = 0 clock_factor = 1 tick_in_usec = 1 wee = 1000 _first_letter = re.compile('[^0-9]+') 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 code is ported from tc utility return int(time) * tick_in_usec def calc_xmittime(rate, size): # The 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 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 code is ported from tc utility i = qmax - qmin assert i > 0 probability /= i for i in range(32): if probability > 1: break probability *= 2 return i def red_eval_idle_damping(Wlog, avpkt, bps): # The code is ported from tc utility xmit_time = calc_xmittime(bps, avpkt) lW = -logfm(1.0 - 1.0 / (1 << Wlog)) / xmit_time maxtime = 31.0 / lW sbuf = [] for clog in range(32): if (maxtime / (1 << clog) < 512): break if clog >= 32: return -1, sbuf for i in range(255): sbuf.append((i << clog) * lW) if sbuf[i] > 31: sbuf[i] = 31 sbuf.append(31) return clog, sbuf 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)} tc_actions = {'unspec': -1, # TC_ACT_UNSPEC 'ok': 0, # TC_ACT_OK 'reclassify': 1, # TC_ACT_RECLASSIFY 'shot': 2, # TC_ACT_SHOT 'drop': 2, # TC_ACT_SHOT 'pipe': 3, # TC_ACT_PIPE 'stolen': 4, # TC_ACT_STOLEN 'queued': 5, # TC_ACT_QUEUED 'repeat': 6, # TC_ACT_REPEAT 'redirect': 7, # TC_ACT_REDIRECT } 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'), ) own_parent = True 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 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', 'stats_app')) 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 stats_app(nla.hex): pass pyroute2-0.4.21/pyroute2/netlink/rtnl/tcmsg/sched_clsact.py0000664000175000017500000000176213123443516023637 0ustar peetpeet00000000000000''' clsact ++++++ The clsact qdisc provides a mechanism to attach integrated filter-action classifiers to an interface, either at ingress or egress, or both. The use case shown here is using a bpf program (implemented elsewhere) to direct the packet processing. The example also uses the direct-action feature to specify what to do with each packet (pass, drop, redirect, etc.). BPF ingress/egress example using clsact qdisc:: # open_bpf_fd is outside the scope of pyroute2 #fd = open_bpf_fd() eth0 = ip.get_links(ifname="eth0")[0] ip.tc("add", "clsact", eth0) # add ingress clsact ip.tc("add-filter", "bpf", idx, ":1", fd=fd, name="myprog", parent="ffff:fff2", classid=1, direct_action=True) # add egress clsact ip.tc("add-filter", "bpf", idx, ":1", fd=fd, name="myprog", parent="ffff:fff3", classid=1, direct_action=True) ''' from pyroute2.netlink.rtnl import TC_H_CLSACT parent = TC_H_CLSACT def fix_msg(msg, kwarg): msg['handle'] = 0xffff0000 pyroute2-0.4.21/pyroute2/netlink/rtnl/tcmsg/act_bpf.py0000664000175000017500000000175713123443516022622 0ustar peetpeet00000000000000from pyroute2.netlink import nla from pyroute2.netlink.rtnl.tcmsg.common import tc_actions class options(nla): nla_map = (('TCA_ACT_BPF_UNSPEC', 'none'), ('TCA_ACT_BPF_TM,', 'none'), ('TCA_ACT_BPF_PARMS', 'tca_act_bpf_parms'), ('TCA_ACT_BPF_OPS_LEN', 'uint16'), ('TCA_ACT_BPF_OPS', 'hex'), ('TCA_ACT_BPF_FD', 'uint32'), ('TCA_ACT_BPF_NAME', 'asciiz')) class tca_act_bpf_parms(nla): fields = (('index', 'I'), ('capab', 'I'), ('action', 'i'), ('refcnt', 'i'), ('bindcnt', 'i')) def get_parameters(kwarg): ret = {'attrs': []} if 'fd' in kwarg: ret['attrs'].append(['TCA_ACT_BPF_FD', kwarg['fd']]) if 'name' in kwarg: ret['attrs'].append(['TCA_ACT_BPF_NAME', kwarg['name']]) a = tc_actions[kwarg.get('action', 'drop')] ret['attrs'].append(['TCA_ACT_BPF_PARMS', {'action': a}]) return ret pyroute2-0.4.21/pyroute2/netlink/rtnl/tcmsg/sched_plug.py0000664000175000017500000000071713123443516023334 0ustar peetpeet00000000000000from pyroute2.netlink import nla from pyroute2.netlink.rtnl import TC_H_ROOT parent = TC_H_ROOT actions = {'TCQ_PLUG_BUFFER': 0, 'TCQ_PLUG_RELEASE_ONE': 1, 'TCQ_PLUG_RELEASE_INDEFINITE': 2, 'TCQ_PLUG_LIMIT': 3} def get_parameters(kwarg): return {'action': actions.get(kwarg.get('action', 0), 0), 'limit': kwarg.get('limit', 0)} class options(nla): fields = (('action', 'i'), ('limit', 'I')) pyroute2-0.4.21/pyroute2/netlink/rtnl/tcmsg/sched_htb.py0000664000175000017500000001233013127143031023125 0ustar peetpeet00000000000000''' htb +++ TODO: list parameters An 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"]) ''' from pyroute2.netlink.rtnl.tcmsg.common import get_hz from pyroute2.netlink.rtnl.tcmsg.common import get_rate from pyroute2.netlink.rtnl.tcmsg.common import calc_xmittime from pyroute2.netlink.rtnl.tcmsg.common import nla_plus_rtab from pyroute2.netlink import nla from pyroute2.netlink.rtnl import TC_H_ROOT parent = TC_H_ROOT def get_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_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 fix_msg(msg, kwarg): if not kwarg: opts = get_parameters({}) msg['attrs'].append(['TCA_OPTIONS', opts]) class stats(nla): fields = (('lends', 'I'), ('borrows', 'I'), ('giants', 'I'), ('tokens', 'I'), ('ctokens', 'I')) class options(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')) pyroute2-0.4.21/pyroute2/netlink/rtnl/tcmsg/act_police.py0000664000175000017500000000405113123443516023314 0ustar peetpeet00000000000000from pyroute2.netlink.rtnl.tcmsg.common import nla_plus_rtab from pyroute2.netlink.rtnl.tcmsg.common import get_rate_parameters 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 options(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')) class nla_plus_police(object): class police(options): pass def get_parameters(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 tbfp['action'] = actions[kwarg.get('action', 'reclassify')] return {'attrs': [['TCA_POLICE_TBF', tbfp], ['TCA_POLICE_RATE', True]]} pyroute2-0.4.21/pyroute2/netlink/rtnl/tcmsg/sched_pfifo_fast.py0000664000175000017500000000036113152305337024500 0ustar peetpeet00000000000000from pyroute2.netlink import nla from pyroute2.netlink.rtnl import TC_H_ROOT parent = TC_H_ROOT class options(nla): fields = (('bands', 'i'), ('priomap', '16B')) def get_parameters(kwarg): return kwarg pyroute2-0.4.21/pyroute2/netlink/rtnl/tcmsg/cls_fw.py0000664000175000017500000000227213123443516022472 0ustar peetpeet00000000000000from socket import htons from pyroute2.netlink import nla from pyroute2.netlink.rtnl.tcmsg.act_police import nla_plus_police from pyroute2.netlink.rtnl.tcmsg.act_police import get_parameters \ as ap_parameters def fix_msg(msg, kwarg): msg['info'] = htons(kwarg.get('protocol', 0) & 0xffff) |\ ((kwarg.get('prio', 0) << 16) & 0xffff0000) def get_parameters(kwarg): ret = {'attrs': []} attrs_map = ( ('classid', 'TCA_FW_CLASSID'), ('act', 'TCA_FW_ACT'), # ('police', 'TCA_FW_POLICE'), # Handled in ap_parameters ('indev', 'TCA_FW_INDEV'), ('mask', 'TCA_FW_MASK'), ) if kwarg.get('rate'): ret['attrs'].append(['TCA_FW_POLICE', ap_parameters(kwarg)]) for k, v in attrs_map: r = kwarg.get(k, None) if r is not None: ret['attrs'].append([v, r]) return ret class options(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')) pyroute2-0.4.21/pyroute2/netlink/rtnl/tcmsg/common_act.py0000664000175000017500000000321013152305337023325 0ustar peetpeet00000000000000from pyroute2.netlink.rtnl.tcmsg import act_gact from pyroute2.netlink.rtnl.tcmsg import act_bpf from pyroute2.netlink.rtnl.tcmsg import act_police from pyroute2.netlink.rtnl.tcmsg import act_mirred from pyroute2.netlink.rtnl.tcmsg import act_connmark from pyroute2.netlink.rtnl.tcmsg import act_vlan plugins = {'gact': act_gact, 'bpf': act_bpf, 'police': act_police, 'mirred': act_mirred, 'connmark': act_connmark, 'vlan': act_vlan, } class nla_plus_tca_act_opt(object): @staticmethod def get_act_options(self, *argv, **kwarg): kind = self.get_attr('TCA_ACT_KIND') if kind in plugins: return plugins[kind].options else: return self.hex return self.hex def get_act_parms(kwarg): if 'kind' not in kwarg: raise Exception('action requires "kind" parameter') if kwarg['kind'] in plugins: return plugins[kwarg['kind']].get_parameters(kwarg) else: return [] # All filters can use any act type, this is a generic parser for all def get_tca_action(kwarg): ret = {'attrs': []} act = kwarg.get('action', 'drop') # convert simple action='..' to kwarg style if isinstance(act, str): act = {'kind': 'gact', 'action': act} # convert single dict action to first entry in a list of actions acts = act if isinstance(act, list) else [act] for i, act in enumerate(acts, start=1): opt = {'attrs': [['TCA_ACT_KIND', act['kind']], ['TCA_ACT_OPTIONS', get_act_parms(act)]]} ret['attrs'].append(['TCA_ACT_PRIO_%d' % i, opt]) return ret pyroute2-0.4.21/pyroute2/netlink/rtnl/req.py0000664000175000017500000003575413130477003020676 0ustar peetpeet00000000000000from socket import AF_INET from socket import AF_INET6 from pyroute2.common import AF_MPLS from pyroute2.common import basestring from pyroute2.netlink.rtnl import rt_type from pyroute2.netlink.rtnl import rt_proto from pyroute2.netlink.rtnl import rt_scope from pyroute2.netlink.rtnl import encap_type from pyroute2.netlink.rtnl.ifinfmsg import ifinfmsg from pyroute2.netlink.rtnl.ifinfmsg import protinfo_bridge from pyroute2.netlink.rtnl.ifinfmsg.plugins.vlan import flags as vlan_flags from pyroute2.netlink.rtnl.rtmsg import rtmsg from pyroute2.netlink.rtnl.rtmsg import nh as nh_header from pyroute2.netlink.rtnl.fibmsg import FR_ACT_NAMES encap_types = {'mpls': 1, AF_MPLS: 1} class IPRequest(dict): def __init__(self, obj=None): dict.__init__(self) if obj is not None: self.update(obj) def update(self, obj): if obj.get('family', None): self['family'] = obj['family'] for key in obj: if key == 'family': continue v = obj[key] if isinstance(v, dict): self[key] = dict((x for x in v.items() if x[1] is not None)) elif v is not None: self[key] = v class IPRuleRequest(IPRequest): def update(self, obj): super(IPRuleRequest, self).update(obj) # now fix the rest if 'family' not in self: self['family'] = AF_INET if 'priority' not in self: self['priority'] = 32000 if 'table' in self and 'action' not in self: self['action'] = 'to_tbl' for key in ('src_len', 'dst_len'): if self.get(key, None) is None and key[:3] in self: self[key] = {AF_INET6: 128, AF_INET: 32}[self['family']] def __setitem__(self, key, value): if key.startswith('ipdb_'): return if key in ('src', 'dst'): v = value.split('/') if len(v) == 2: value, self['%s_len' % key] = v[0], int(v[1]) elif key == 'action' and isinstance(value, basestring): value = (FR_ACT_NAMES .get(value, (FR_ACT_NAMES .get('FR_ACT_' + value.upper(), value)))) dict.__setitem__(self, key, value) class IPRouteRequest(IPRequest): ''' Utility class, that converts human-readable dictionary into RTNL route request. ''' resolve = {'encap_type': encap_type, 'type': rt_type, 'proto': rt_proto, 'scope': rt_scope} def encap_header(self, header): ''' Encap header transform. Format samples: {'type': 'mpls', 'labels': '200/300'} {'type': AF_MPLS, 'labels': (200, 300)} {'type': 'mpls', 'labels': 200} {'type': AF_MPLS, 'labels': [{'bos': 0, 'label': 200, 'ttl': 16}, {'bos': 1, 'label': 300, 'ttl': 16}]} ''' if isinstance(header['type'], int) or \ (header['type'] in ('mpls', AF_MPLS)): ret = [] override_bos = True labels = header['labels'] if isinstance(labels, basestring): labels = labels.split('/') if not isinstance(labels, (tuple, list, set)): labels = (labels, ) for label in labels: if isinstance(label, dict): # dicts append intact override_bos = False ret.append(label) else: # otherwise construct label dict if isinstance(label, basestring): label = int(label) ret.append({'bos': 0, 'label': label}) # the last label becomes bottom-of-stack if override_bos: ret[-1]['bos'] = 1 return {'attrs': [['MPLS_IPTUNNEL_DST', ret]]} def mpls_rta(self, value): ret = [] if not isinstance(value, (list, tuple, set)): value = (value, ) for label in value: if isinstance(label, int): label = {'label': label, 'bos': 0} elif isinstance(label, basestring): label = {'label': int(label), 'bos': 0} elif not isinstance(label, dict): raise ValueError('wrong MPLS label') ret.append(label) if ret: ret[-1]['bos'] = 1 return ret def __setitem__(self, key, value): # skip virtual IPDB fields if key.startswith('ipdb_'): return # fix family if isinstance(value, basestring) and value.find(':') >= 0: self['family'] = AF_INET6 # work on the rest if key == 'family' and value == AF_MPLS: dict.__setitem__(self, 'family', value) dict.__setitem__(self, 'dst_len', 20) dict.__setitem__(self, 'table', 254) dict.__setitem__(self, 'type', 1) elif key == 'flags' and self.get('family', None) == AF_MPLS: return elif key == 'dst': if isinstance(value, dict): dict.__setitem__(self, 'dst', value) elif isinstance(value, int): dict.__setitem__(self, 'dst', {'label': value, 'bos': 1}) elif 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) if mask: dict.__setitem__(self, 'dst_len', mask) elif key == 'newdst': dict.__setitem__(self, 'newdst', self.mpls_rta(value)) elif key in self.resolve.keys(): if isinstance(value, basestring): value = self.resolve[key][value] dict.__setitem__(self, key, value) elif key == 'encap': if isinstance(value, dict): # human-friendly form: # # 'encap': {'type': 'mpls', # 'labels': '200/300'} # # 'type' is mandatory if 'type' in value and 'labels' in value: dict.__setitem__(self, 'encap_type', encap_types.get(value['type'], value['type'])) dict.__setitem__(self, 'encap', self.encap_header(value)) # assume it is a ready-to-use NLA elif 'attrs' in value: dict.__setitem__(self, 'encap', value) elif key == 'via': # ignore empty RTA_VIA if isinstance(value, dict) and \ set(value.keys()) == set(('addr', 'family')) and \ value['family'] in (AF_INET, AF_INET6) and \ isinstance(value['addr'], basestring): dict.__setitem__(self, 'via', value) elif key == 'metrics': if 'attrs' in value: ret = value else: ret = {'attrs': []} for name in value: rtax = rtmsg.metrics.name2nla(name) ret['attrs'].append([rtax, value[name]]) if ret['attrs']: dict.__setitem__(self, 'metrics', ret) elif key == 'multipath': ret = [] for v in value: if 'attrs' in v: ret.append(v) continue nh = {'attrs': []} nh_fields = [x[0] for x in nh_header.fields] for name in nh_fields: nh[name] = v.get(name, 0) for name in v: if name in nh_fields or v[name] is None: continue if name == 'encap' and isinstance(v[name], dict): if v[name].get('type', None) is None or \ v[name].get('labels', None) is None: continue nh['attrs'].append(['RTA_ENCAP_TYPE', encap_types.get(v[name]['type'], v[name]['type'])]) nh['attrs'].append(['RTA_ENCAP', self.encap_header(v[name])]) elif name == 'newdst': nh['attrs'].append(['RTA_NEWDST', self.mpls_rta(v[name])]) else: rta = rtmsg.name2nla(name) nh['attrs'].append([rta, v[name]]) ret.append(nh) if ret: dict.__setitem__(self, 'multipath', 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 IPBridgeRequest(IPRequest): def __setitem__(self, key, value): if key in ('vlan_info', 'mode', 'vlan_flags'): if 'IFLA_AF_SPEC' not in self: dict.__setitem__(self, 'IFLA_AF_SPEC', {'attrs': []}) nla = ifinfmsg.af_spec_bridge.name2nla(key) self['IFLA_AF_SPEC']['attrs'].append([nla, value]) else: dict.__setitem__(self, key, value) class IPBrPortRequest(dict): def __init__(self, obj=None): dict.__init__(self) dict.__setitem__(self, 'attrs', []) self.allowed = [x[0] for x in protinfo_bridge.nla_map] if obj is not None: self.update(obj) def update(self, obj): for key in obj: self[key] = obj[key] def __setitem__(self, key, value): key = protinfo_bridge.name2nla(key) if key in self.allowed: self['attrs'].append((key, value)) class IPLinkRequest(IPRequest): ''' Utility class, that converts human-readable dictionary into RTNL link request. ''' blacklist = ['carrier', 'carrier_changes'] # get common ifinfmsg NLAs common = [] for (key, _) in ifinfmsg.nla_map: common.append(key) common.append(key[len(ifinfmsg.prefix):].lower()) common.append('family') common.append('ifi_type') common.append('index') common.append('flags') common.append('change') def __init__(self, *argv, **kwarg): self.deferred = [] self.kind = None self.specific = {} self.linkinfo = None self._info_data = None IPRequest.__init__(self, *argv, **kwarg) if 'index' not in self: self['index'] = 0 @property def info_data(self): if self._info_data is None: info_data = ('IFLA_INFO_DATA', {'attrs': []}) self._info_data = info_data[1]['attrs'] self.linkinfo.append(info_data) return self._info_data def flush_deferred(self): # create IFLA_LINKINFO linkinfo = {'attrs': []} self.linkinfo = linkinfo['attrs'] dict.__setitem__(self, 'IFLA_LINKINFO', linkinfo) self.linkinfo.append(['IFLA_INFO_KIND', self.kind]) # load specific NLA names cls = ifinfmsg.ifinfo.data_map.get(self.kind, None) if cls is not None: prefix = cls.prefix or 'IFLA_' for nla, _ in cls.nla_map: self.specific[nla] = nla self.specific[nla[len(prefix):].lower()] = nla # flush deferred NLAs for (key, value) in self.deferred: if not self.set_specific(key, value): dict.__setitem__(self, key, value) self.deferred = [] def set_specific(self, key, value): # FIXME: vlan hack if self.kind == 'vlan' and key == 'vlan_flags': if isinstance(value, (list, tuple)): if len(value) == 2 and \ all((isinstance(x, int) for x in value)): value = {'flags': value[0], 'mask': value[1]} else: ret = 0 for x in value: ret |= vlan_flags.get(x, 1) value = {'flags': ret, 'mask': ret} elif isinstance(value, int): value = {'flags': value, 'mask': value} elif isinstance(value, basestring): value = vlan_flags.get(value, 1) value = {'flags': value, 'mask': value} elif not isinstance(value, dict): raise ValueError() # the kind is known: lookup the NLA if key in self.specific: self.info_data.append((self.specific[key], value)) return True elif key == 'peer' and self.kind == 'veth': # FIXME: veth hack if isinstance(value, dict): attrs = [] for k, v in value.items(): attrs.append([ifinfmsg.name2nla(k), v]) else: attrs = [['IFLA_IFNAME', value], ] nla = ['VETH_INFO_PEER', {'attrs': attrs}] self.info_data.append(nla) return True elif key == 'mode': # FIXME: ipvlan / tuntap / bond hack if self.kind == 'tuntap': nla = ['IFTUN_MODE', value] else: nla = ['IFLA_%s_MODE' % self.kind.upper(), value] self.info_data.append(nla) return True return False 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 if key == 'kind' and not self.kind: self.kind = value self.flush_deferred() elif self.kind is None: if key in self.common: dict.__setitem__(self, key, value) else: self.deferred.append((key, value)) else: if not self.set_specific(key, value): dict.__setitem__(self, key, value) pyroute2-0.4.21/pyroute2/netlink/rtnl/ifaddrmsg.py0000664000175000017500000000537213123443516022044 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_MCAUTOJOIN = 0x400 IFA_F_STABLE_PRIVACY = 0x800 (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 C structure:: 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.4.21/pyroute2/netlink/rtnl/__init__.py0000664000175000017500000001206513123443516021640 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.common import map_namespace # 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 RTNLGRP_MPLS_ROUTE = 0x4000000 # 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_NAMES, RTM_VALUES) = map_namespace('RTM', globals()) TC_H_INGRESS = 0xfffffff1 TC_H_CLSACT = TC_H_INGRESS TC_H_ROOT = 0xffffffff RTNL_GROUPS = RTNLGRP_IPV4_IFADDR |\ RTNLGRP_IPV6_IFADDR |\ RTNLGRP_IPV4_ROUTE |\ RTNLGRP_IPV6_ROUTE |\ RTNLGRP_IPV4_RULE |\ RTNLGRP_IPV6_RULE |\ RTNLGRP_NEIGH |\ RTNLGRP_LINK |\ RTNLGRP_TC |\ RTNLGRP_MPLS_ROUTE encap_type = {'unspec': 0, 'mpls': 1, 0: 'unspec', 1: 'mpls'} 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 # normalized rt_type = dict([(x[0][4:].lower(), x[1]) for x in rtypes.items()] + [(x[1], x[0][4:].lower()) for x in rtypes.items()]) 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 # normalized rt_proto = dict([(x[0][7:].lower(), x[1]) for x in rtprotos.items()] + [(x[1], x[0][7:].lower()) for x in rtprotos.items()]) rtscopes = {'RT_SCOPE_UNIVERSE': 0, 'RT_SCOPE_SITE': 200, 'RT_SCOPE_LINK': 253, 'RT_SCOPE_HOST': 254, 'RT_SCOPE_NOWHERE': 255} # normalized rt_scope = dict([(x[0][9:].lower(), x[1]) for x in rtscopes.items()] + [(x[1], x[0][9:].lower()) for x in rtscopes.items()]) pyroute2-0.4.21/pyroute2/netlink/rtnl/errmsg.py0000664000175000017500000000023313123443516021372 0ustar peetpeet00000000000000from pyroute2.netlink import nlmsg class errmsg(nlmsg): ''' Custom message type Error ersatz-message ''' fields = (('code', 'i'), ) pyroute2-0.4.21/pyroute2/netlink/rtnl/ndmsg.py0000664000175000017500000000507313127143031021203 0ustar peetpeet00000000000000from pyroute2.common import map_namespace from pyroute2.netlink import nlmsg from pyroute2.netlink import nla # neighbor cache entry flags NTF_USE = 0x01 NTF_SELF = 0x02 NTF_MASTER = 0x04 NTF_PROXY = 0x08 NTF_EXT_LEARNED = 0x10 NTF_ROUTER = 0x80 # neighbor cache entry states NUD_INCOMPLETE = 0x01 NUD_REACHABLE = 0x02 NUD_STALE = 0x04 NUD_DELAY = 0x08 NUD_PROBE = 0x10 NUD_FAILED = 0x20 # dummy states NUD_NOARP = 0x40 NUD_PERMANENT = 0x80 NUD_NONE = 0x00 (NTF_NAMES, NTF_VALUES) = map_namespace('NTF_', globals()) (NUD_NAMES, NUD_VALUES) = map_namespace('NUD_', globals()) flags = dict([(x[0][4:].lower(), x[1]) for x in NTF_NAMES.items()]) states = dict([(x[0][4:].lower(), x[1]) for x in NUD_NAMES.items()]) def states_a2n(s): # parse state string ss = s.split(',') ret = 0 for state in ss: state = state.upper() if not state.startswith('NUD_'): state = 'NUD_' + state ret |= NUD_NAMES[state] return ret class ndmsg(nlmsg): ''' ARP cache update message C structure:: struct ndmsg { unsigned char ndm_family; int ndm_ifindex; /* Interface index */ __u16 ndm_state; /* State */ __u8 ndm_flags; /* Flags */ __u8 ndm_type; }; Cache info structure:: struct nda_cacheinfo { __u32 ndm_confirmed; __u32 ndm_used; __u32 ndm_updated; __u32 ndm_refcnt; }; ''' __slots__ = () prefix = 'NDA_' fields = (('family', 'B'), ('__pad', '3x'), ('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', 'uint32'), ('NDA_IFINDEX', 'uint32'), ('NDA_MASTER', 'uint32')) class cacheinfo(nla): __slots__ = () fields = (('ndm_confirmed', 'I'), ('ndm_used', 'I'), ('ndm_updated', 'I'), ('ndm_refcnt', 'I')) pyroute2-0.4.21/pyroute2/netlink/rtnl/fibmsg.py0000664000175000017500000000334013123443516021344 0ustar peetpeet00000000000000 from pyroute2.common import map_namespace from pyroute2.netlink import nlmsg FR_ACT_UNSPEC = 0 FR_ACT_TO_TBL = 1 FR_ACT_GOTO = 2 FR_ACT_NOP = 3 FR_ACT_BLACKHOLE = 6 FR_ACT_UNREACHABLE = 7 FR_ACT_PROHIBIT = 8 (FR_ACT_NAMES, FR_ACT_VALUES) = map_namespace('FR_ACT', globals()) class fibmsg(nlmsg): ''' IP rule message C structure:: struct fib_rule_hdr { __u8 family; __u8 dst_len; __u8 src_len; __u8 tos; __u8 table; __u8 res1; /* reserved */ __u8 res2; /* reserved */ __u8 action; __u32 flags; }; ''' prefix = 'FRA_' fields = (('family', 'B'), ('dst_len', 'B'), ('src_len', 'B'), ('tos', 'B'), ('table', 'B'), ('res1', 'B'), ('res2', 'B'), ('action', 'B'), ('flags', 'I')) # fibmsg NLA numbers are not sequential, so # give them here explicitly nla_map = ((0, 'FRA_UNSPEC', 'none'), (1, 'FRA_DST', 'ipaddr'), (2, 'FRA_SRC', 'ipaddr'), (3, 'FRA_IIFNAME', 'asciiz'), (4, 'FRA_GOTO', 'uint32'), (6, 'FRA_PRIORITY', 'uint32'), (10, 'FRA_FWMARK', 'uint32'), (11, 'FRA_FLOW', 'uint32'), (12, 'FRA_TUN_ID', 'be64'), (13, 'FRA_SUPPRESS_IFGROUP', 'uint32'), (14, 'FRA_SUPPRESS_PREFIXLEN', 'uint32'), (15, 'FRA_TABLE', 'uint32'), (16, 'FRA_FWMASK', 'uint32'), (17, 'FRA_OIFNAME', 'asciiz'), (18, 'FRA_PAD', 'hex')) pyroute2-0.4.21/pyroute2/netlink/rtnl/iprsocket.py0000664000175000017500000001337413152305337022110 0ustar peetpeet00000000000000 from pyroute2 import config from pyroute2.common import Namespace from pyroute2.common import AddrPool from pyroute2.proxy import NetlinkProxy from pyroute2.netlink import NETLINK_ROUTE from pyroute2.netlink.nlsocket import NetlinkSocket from pyroute2.netlink.nlsocket import BatchSocket from pyroute2.netlink import rtnl from pyroute2.netlink.rtnl.marshal import MarshalRtnl if config.kernel < [3, 3, 0]: from pyroute2.netlink.rtnl.ifinfmsg.compat import proxy_newlink from pyroute2.netlink.rtnl.ifinfmsg.compat import proxy_setlink from pyroute2.netlink.rtnl.ifinfmsg.compat import proxy_dellink from pyroute2.netlink.rtnl.ifinfmsg.compat import proxy_linkinfo else: from pyroute2.netlink.rtnl.ifinfmsg.proxy import proxy_newlink from pyroute2.netlink.rtnl.ifinfmsg.proxy import proxy_setlink class IPRSocketMixin(object): def __init__(self, fileno=None, all_ns=False): super(IPRSocketMixin, self).__init__(NETLINK_ROUTE, fileno=fileno, all_ns=all_ns) self.marshal = MarshalRtnl() self._s_channel = None send_ns = Namespace(self, {'addr_pool': AddrPool(0x10000, 0x1ffff), 'monitor': False}) self._sproxy = NetlinkProxy(policy='return', nl=send_ns) self._sproxy.pmap = {rtnl.RTM_NEWLINK: proxy_newlink, rtnl.RTM_SETLINK: proxy_setlink} if config.kernel < [3, 3, 0]: self._recv_ns = Namespace(self, {'addr_pool': AddrPool(0x20000, 0x2ffff), 'monitor': False}) self._sproxy.pmap[rtnl.RTM_DELLINK] = proxy_dellink # inject proxy hooks into recv() and... self.__recv = self._recv self._recv = self._p_recv # ... recv_into() self._recv_ft = self.recv_ft self.recv_ft = self._p_recv_ft def clone(self): return type(self)() def bind(self, groups=rtnl.RTNL_GROUPS, async=False): super(IPRSocketMixin, self).bind(groups, async=async) def _gate(self, msg, addr): msg.reset() msg.encode() ret = self._sproxy.handle(msg) if ret is not None: if ret['verdict'] == 'forward': return self._sendto(ret['data'], addr) 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(msg.data, addr) def _p_recv_ft(self, bufsize, flags=0): data = self._recv_ft(bufsize, flags) ret = proxy_linkinfo(data, self._recv_ns) if ret is not None: if ret['verdict'] in ('forward', 'error'): return ret['data'] else: ValueError('Incorrect verdict') return data def _p_recv(self, bufsize, flags=0): data = self.__recv(bufsize, flags) ret = proxy_linkinfo(data, self._recv_ns) if ret is not None: if ret['verdict'] in ('forward', 'error'): return ret['data'] else: ValueError('Incorrect verdict') return data class IPBatchSocket(IPRSocketMixin, BatchSocket): pass 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.4.21/pyroute2/netlink/rtnl/iw_event.py0000664000175000017500000000730013123443516021715 0ustar peetpeet00000000000000from pyroute2.netlink import nla class iw_event(nla): nla_map = ((0xB00, 'SIOCSIWCOMMIT', 'hex'), (0xB01, 'SIOCGIWNAME', 'hex'), # Basic operations (0xB02, 'SIOCSIWNWID', 'hex'), (0xB03, 'SIOCGIWNWID', 'hex'), (0xB04, 'SIOCSIWFREQ', 'hex'), (0xB05, 'SIOCGIWFREQ', 'hex'), (0xB06, 'SIOCSIWMODE', 'hex'), (0xB07, 'SIOCGIWMODE', 'hex'), (0xB08, 'SIOCSIWSENS', 'hex'), (0xB09, 'SIOCGIWSENS', 'hex'), # Informative stuff (0xB0A, 'SIOCSIWRANGE', 'hex'), (0xB0B, 'SIOCGIWRANGE', 'hex'), (0xB0C, 'SIOCSIWPRIV', 'hex'), (0xB0D, 'SIOCGIWPRIV', 'hex'), (0xB0E, 'SIOCSIWSTATS', 'hex'), (0xB0F, 'SIOCGIWSTATS', 'hex'), # Spy support (statistics per MAC address - # used for Mobile IP support) (0xB10, 'SIOCSIWSPY', 'hex'), (0xB11, 'SIOCGIWSPY', 'hex'), (0xB12, 'SIOCSIWTHRSPY', 'hex'), (0xB13, 'SIOCGIWTHRSPY', 'hex'), # Access Point manipulation (0xB14, 'SIOCSIWAP', 'hex'), (0xB15, 'SIOCGIWAP', 'hex'), (0xB17, 'SIOCGIWAPLIST', 'hex'), (0xB18, 'SIOCSIWSCAN', 'hex'), (0xB19, 'SIOCGIWSCAN', 'hex'), # 802.11 specific support (0xB1A, 'SIOCSIWESSID', 'hex'), (0xB1B, 'SIOCGIWESSID', 'hex'), (0xB1C, 'SIOCSIWNICKN', 'hex'), (0xB1D, 'SIOCGIWNICKN', 'hex'), # Other parameters useful in 802.11 and # some other devices (0xB20, 'SIOCSIWRATE', 'hex'), (0xB21, 'SIOCGIWRATE', 'hex'), (0xB22, 'SIOCSIWRTS', 'hex'), (0xB23, 'SIOCGIWRTS', 'hex'), (0xB24, 'SIOCSIWFRAG', 'hex'), (0xB25, 'SIOCGIWFRAG', 'hex'), (0xB26, 'SIOCSIWTXPOW', 'hex'), (0xB27, 'SIOCGIWTXPOW', 'hex'), (0xB28, 'SIOCSIWRETRY', 'hex'), (0xB29, 'SIOCGIWRETRY', 'hex'), # Encoding stuff (scrambling, hardware security, WEP...) (0xB2A, 'SIOCSIWENCODE', 'hex'), (0xB2B, 'SIOCGIWENCODE', 'hex'), # Power saving stuff (power management, unicast # and multicast) (0xB2C, 'SIOCSIWPOWER', 'hex'), (0xB2D, 'SIOCGIWPOWER', 'hex'), # WPA : Generic IEEE 802.11 informatiom element # (e.g., for WPA/RSN/WMM). (0xB30, 'SIOCSIWGENIE', 'hex'), (0xB31, 'SIOCGIWGENIE', 'hex'), # WPA : IEEE 802.11 MLME requests (0xB16, 'SIOCSIWMLME', 'hex'), # WPA : Authentication mode parameters (0xB32, 'SIOCSIWAUTH', 'hex'), (0xB33, 'SIOCGIWAUTH', 'hex'), # WPA : Extended version of encoding configuration (0xB34, 'SIOCSIWENCODEEXT', 'hex'), (0xB35, 'SIOCGIWENCODEEXT', 'hex'), # WPA2 : PMKSA cache management (0xB36, 'SIOCSIWPMKSA', 'hex'), # Events s.str. (0xC00, 'IWEVTXDROP', 'hex'), (0xC01, 'IWEVQUAL', 'hex'), (0xC02, 'IWEVCUSTOM', 'hex'), (0xC03, 'IWEVREGISTERED', 'hex'), (0xC04, 'IWEVEXPIRED', 'hex'), (0xC05, 'IWEVGENIE', 'hex'), (0xC06, 'IWEVMICHAELMICFAILURE', 'hex'), (0xC07, 'IWEVASSOCREQIE', 'hex'), (0xC08, 'IWEVASSOCRESPIE', 'hex'), (0xC09, 'IWEVPMKIDCAND', 'hex')) pyroute2-0.4.21/pyroute2/netlink/rtnl/ifinfmsg/0000775000175000017500000000000013152333366021330 5ustar peetpeet00000000000000pyroute2-0.4.21/pyroute2/netlink/rtnl/ifinfmsg/plugins/0000775000175000017500000000000013152333366023011 5ustar peetpeet00000000000000pyroute2-0.4.21/pyroute2/netlink/rtnl/ifinfmsg/plugins/ipvlan.py0000664000175000017500000000043513127143031024644 0ustar peetpeet00000000000000from pyroute2.netlink import nla class ipvlan(nla): nla_map = (('IFLA_IPVLAN_UNSPEC', 'none'), ('IFLA_IPVLAN_MODE', 'uint16')) modes = {0: 'IPVLAN_MODE_L2', 1: 'IPVLAN_MODE_L3', 'IPVLAN_MODE_L2': 0, 'IPVLAN_MODE_L3': 1} pyroute2-0.4.21/pyroute2/netlink/rtnl/ifinfmsg/plugins/bond.py0000664000175000017500000000370513127143031024300 0ustar peetpeet00000000000000from pyroute2.netlink import nla class bond(nla): 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'), ('IFLA_BOND_AD_ACTOR_SYS_PRIO', 'uint16'), ('IFLA_BOND_AD_USER_PORT_KEY', 'uint16'), ('IFLA_BOND_AD_ACTOR_SYSTEM', 'hex'), ('IFLA_BOND_TLB_DYNAMIC_LB', 'uint8')) 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'), ) pyroute2-0.4.21/pyroute2/netlink/rtnl/ifinfmsg/plugins/tuntap.py0000664000175000017500000000112213127143031024660 0ustar peetpeet00000000000000from pyroute2.netlink import nla class tuntap(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')) pyroute2-0.4.21/pyroute2/netlink/rtnl/ifinfmsg/plugins/vrf.py0000664000175000017500000000021413127143031024143 0ustar peetpeet00000000000000from pyroute2.netlink import nla class vrf(nla): nla_map = (('IFLA_VRF_UNSPEC', 'none'), ('IFLA_VRF_TABLE', 'uint32')) pyroute2-0.4.21/pyroute2/netlink/rtnl/ifinfmsg/plugins/vxlan.py0000664000175000017500000000261213127143031024502 0ustar peetpeet00000000000000from pyroute2.netlink import nla class vxlan(nla): nla_map = (('IFLA_VXLAN_UNSPEC', 'none'), ('IFLA_VXLAN_ID', 'uint32'), ('IFLA_VXLAN_GROUP', 'ip4addr'), ('IFLA_VXLAN_LINK', 'uint32'), ('IFLA_VXLAN_LOCAL', 'ip4addr'), ('IFLA_VXLAN_TTL', 'uint8'), ('IFLA_VXLAN_TOS', 'uint8'), ('IFLA_VXLAN_LEARNING', 'uint8'), ('IFLA_VXLAN_AGEING', 'uint32'), ('IFLA_VXLAN_LIMIT', 'uint32'), ('IFLA_VXLAN_PORT_RANGE', 'port_range'), ('IFLA_VXLAN_PROXY', 'uint8'), ('IFLA_VXLAN_RSC', 'uint8'), ('IFLA_VXLAN_L2MISS', 'uint8'), ('IFLA_VXLAN_L3MISS', 'uint8'), ('IFLA_VXLAN_PORT', 'be16'), ('IFLA_VXLAN_GROUP6', 'ip6addr'), ('IFLA_VXLAN_LOCAL6', 'ip6addr'), ('IFLA_VXLAN_UDP_CSUM', 'uint8'), ('IFLA_VXLAN_UDP_ZERO_CSUM6_TX', 'uint8'), ('IFLA_VXLAN_UDP_ZERO_CSUM6_RX', 'uint8'), ('IFLA_VXLAN_REMCSUM_TX', 'uint8'), ('IFLA_VXLAN_REMCSUM_RX', 'uint8'), ('IFLA_VXLAN_GBP', 'flag'), ('IFLA_VXLAN_REMCSUM_NOPARTIAL', 'flag'), ('IFLA_VXLAN_COLLECT_METADATA', 'uint8')) class port_range(nla): fields = (('low', '>H'), ('high', '>H')) pyroute2-0.4.21/pyroute2/netlink/rtnl/ifinfmsg/plugins/mykind.py0000664000175000017500000000034213076060251024650 0ustar peetpeet00000000000000from pyroute2.netlink import nla class mykind(nla): prefix = 'IFLA_MYKIND_' nla_map = (('IFLA_MYKIND_UNSPEC', 'none'), ('IFLA_MYKIND_MYPORT', 'uint32'), ('IFLA_MYKIND_MYDEV', 'asciiz')) pyroute2-0.4.21/pyroute2/netlink/rtnl/ifinfmsg/plugins/__init__.py0000664000175000017500000000000013127143031025076 0ustar peetpeet00000000000000pyroute2-0.4.21/pyroute2/netlink/rtnl/ifinfmsg/plugins/gtp.py0000664000175000017500000000035113127143031024142 0ustar peetpeet00000000000000from pyroute2.netlink import nla class gtp(nla): nla_map = (('IFLA_GTP_UNSPEC', 'none'), ('IFLA_GTP_FD0', 'uint32'), ('IFLA_GTP_FD1', 'uint32'), ('IFLA_GTP_PDP_HASHSIZE', 'uint32')) pyroute2-0.4.21/pyroute2/netlink/rtnl/ifinfmsg/plugins/vti.py0000664000175000017500000000047413127143031024160 0ustar peetpeet00000000000000from pyroute2.netlink import nla class vti(nla): nla_map = (('IFLA_VTI_UNSPEC', 'none'), ('IFLA_VTI_LINK', 'uint32'), ('IFLA_VTI_IKEY', 'be32'), ('IFLA_VTI_OKEY', 'be32'), ('IFLA_VTI_LOCAL', 'ip4addr'), ('IFLA_VTI_REMOTE', 'ip4addr')) pyroute2-0.4.21/pyroute2/netlink/rtnl/ifinfmsg/plugins/vlan.py0000664000175000017500000000140613127143031024312 0ustar peetpeet00000000000000from pyroute2.netlink import nla flags = {'reorder_hdr': 0x1, 'gvrp': 0x2, 'loose_binding': 0x4, 'mvrp': 0x8} class vlan(nla): nla_map = (('IFLA_VLAN_UNSPEC', 'none'), ('IFLA_VLAN_ID', 'uint16'), ('IFLA_VLAN_FLAGS', 'vlan_flags'), ('IFLA_VLAN_EGRESS_QOS', 'qos'), ('IFLA_VLAN_INGRESS_QOS', 'qos'), ('IFLA_VLAN_PROTOCOL', 'be16')) class vlan_flags(nla): fields = (('flags', 'I'), ('mask', 'I')) class qos(nla): nla_map = (('IFLA_VLAN_QOS_UNSPEC', 'none'), ('IFLA_VLAN_QOS_MAPPING', 'qos_mapping')) class qos_mapping(nla): fields = (('from', 'I'), ('to', 'I')) pyroute2-0.4.21/pyroute2/netlink/rtnl/ifinfmsg/__init__.py0000664000175000017500000007463013152305337023450 0ustar peetpeet00000000000000import os import sys import struct import pkgutil import importlib from socket import AF_INET from socket import AF_INET6 from pyroute2 import config from pyroute2.config import AF_BRIDGE from pyroute2.common import map_namespace from pyroute2.common import basestring from pyroute2.netlink import nla from pyroute2.netlink import nlmsg from pyroute2.netlink import nlmsg_atoms from pyroute2.netlink.rtnl.iw_event import iw_event from pyroute2.netlink.rtnl.ifinfmsg import plugins config.data_plugins_pkgs.append(plugins) # 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 # ## # # 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 ## # # vlan filter flags # BRIDGE_VLAN_INFO_MASTER = 0x1 # operate on bridge device BRIDGE_VLAN_INFO_PVID = 0x2 # ingress untagged BRIDGE_VLAN_INFO_UNTAGGED = 0x4 # egress untagged BRIDGE_VLAN_INFO_RANGE_BEGIN = 0x8 # range start BRIDGE_VLAN_INFO_RANGE_END = 0x10 # range end BRIDGE_VLAN_INFO_BRENTRY = 0x20 # global bridge vlan entry (BRIDGE_VLAN_NAMES, BRIDGE_VLAN_VALUES) = \ map_namespace('BRIDGE_VLAN_INFO', globals()) BRIDGE_FLAGS_MASTER = 1 BRIDGE_FLAGS_SELF = 2 (BRIDGE_FLAGS_NAMES, BRIDGE_FLAGS_VALUES) = \ map_namespace('BRIDGE_FLAGS', globals()) 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') def load_plugins_by_path(path): plugins = {} files = set([x.split('.')[0] for x in filter(lambda x: x.endswith(('.py', '.pyc', '.pyo')), os.listdir(path)) if not x.startswith('_')]) sys.path.append(path) for name in files: try: module = __import__(name, globals(), locals(), [], 0) plugins[name] = getattr(module, name) except: pass sys.path.pop() return plugins def load_plugins_by_pkg(pkg): plugin_modules = { name: name.split('.')[-1] for loader, name, ispkg in pkgutil.iter_modules( path=pkg.__path__, prefix=pkg.__name__ + '.') } # Hack to make it compatible with pyinstaller # plugin loading will work with and without pyinstaller # Inspired on: # https://github.com/webcomics/dosage/blob/master/dosagelib/loader.py # see: https://github.com/pyinstaller/pyinstaller/issues/1905 importers = map(pkgutil.get_importer, pkg.__path__) toc = set() for importer in importers: if hasattr(importer, 'toc'): toc |= importer.toc for element in toc: if (element.startswith(pkg.__name__) and element != pkg.__name__): plugin_modules[element] = element.split('.')[-1] return { mod_name: getattr(importlib.import_module(mod_path), mod_name) for mod_path, mod_name in plugin_modules.items() if not mod_name.startswith('_') } data_plugins = {} for pkg in config.data_plugins_pkgs: data_plugins.update(load_plugins_by_pkg(pkg)) for path in config.data_plugins_path: data_plugins.update(load_plugins_by_path(path)) class ifla_bridge_id(nla): fields = [('value', '=8s')] def encode(self): r_prio = struct.pack('H', self['prio']) r_addr = struct.pack('BBBBBB', *[int(i, 16) for i in self['addr'].split(':')]) self['value'] = r_prio + r_addr nla.encode(self) def decode(self): nla.decode(self) r_prio = self['value'][:2] r_addr = self['value'][2:] self.value = {'prio': struct.unpack('H', r_prio)[0], 'addr': ':'.join('%02x' % (i) for i in struct.unpack('BBBBBB', r_addr))} class protinfo_bridge(nla): prefix = 'IFLA_BRPORT_' nla_map = (('IFLA_BRPORT_UNSPEC', 'none'), ('IFLA_BRPORT_STATE', 'uint8'), ('IFLA_BRPORT_PRIORITY', 'uint16'), ('IFLA_BRPORT_COST', 'uint32'), ('IFLA_BRPORT_MODE', 'uint8'), ('IFLA_BRPORT_GUARD', 'uint8'), ('IFLA_BRPORT_PROTECT', 'uint8'), ('IFLA_BRPORT_FAST_LEAVE', 'uint8'), ('IFLA_BRPORT_LEARNING', 'uint8'), ('IFLA_BRPORT_UNICAST_FLOOD', 'uint8'), ('IFLA_BRPORT_PROXYARP', 'uint8'), ('IFLA_BRPORT_LEARNING_SYNC', 'uint8'), ('IFLA_BRPORT_PROXYARP_WIFI', 'uint8'), ('IFLA_BRPORT_ROOT_ID', 'br_id'), ('IFLA_BRPORT_BRIDGE_ID', 'br_id'), ('IFLA_BRPORT_DESIGNATED_PORT', 'uint16'), ('IFLA_BRPORT_DESIGNATED_COST', 'uint16'), ('IFLA_BRPORT_ID', 'uint16'), ('IFLA_BRPORT_NO', 'uint16'), ('IFLA_BRPORT_TOPOLOGY_CHANGE_ACK', 'uint8'), ('IFLA_BRPORT_CONFIG_PENDING', 'uint8'), ('IFLA_BRPORT_MESSAGE_AGE_TIMER', 'uint64'), ('IFLA_BRPORT_FORWARD_DELAY_TIMER', 'uint64'), ('IFLA_BRPORT_HOLD_TIMER', 'uint64'), ('IFLA_BRPORT_FLUSH', 'flag'), ('IFLA_BRPORT_MULTICAST_ROUTER', 'uint8'), ('IFLA_BRPORT_PAD', 'uint64'), ('IFLA_BRPORT_MCAST_FLOOD', 'uint8'), ('IFLA_BRPORT_MCAST_TO_UCAST', 'uint8'), ('IFLA_BRPORT_VLAN_TUNNEL', 'uint8'), ('IFLA_BRPORT_BCAST_FLOOD', 'uint8')) class br_id(ifla_bridge_id): pass class macvx_data(nla): nla_map = (('IFLA_MACVLAN_UNSPEC', 'none'), ('IFLA_MACVLAN_MODE', 'mode'), ('IFLA_MACVLAN_FLAGS', 'flags'), ('IFLA_MACVLAN_MACADDR_MODE', 'macaddr_mode'), ('IFLA_MACVLAN_MACADDR', 'l2addr'), ('IFLA_MACVLAN_MACADDR_DATA', 'macaddr_data'), ('IFLA_MACVLAN_MACADDR_COUNT', 'uint32')) class mode(nlmsg_atoms.uint32): value_map = {0: 'none', 1: 'private', 2: 'vepa', 4: 'bridge', 8: 'passthru', 16: 'source'} class flags(nlmsg_atoms.uint16): value_map = {0: 'none', 1: 'nopromisc'} class macaddr_mode(nlmsg_atoms.uint32): value_map = {0: 'add', 1: 'del', 2: 'flush', 3: 'set'} class macaddr_data(nla): nla_map = ((4, 'IFLA_MACVLAN_MACADDR', 'l2addr'), ) class ifinfbase(object): ''' Network interface message. C structure:: 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', 'x'), ('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', 'protinfo'), ('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', 'asciiz'), ('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', 'uint32'), ('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'), ('IFLA_PHYS_SWITCH_ID', 'hex'), ('IFLA_LINK_NETNSID', 'int32'), ('IFLA_PHYS_PORT_NAME', 'asciiz'), ('IFLA_PROTO_DOWN', 'uint8'), ('IFLA_GSO_MAX_SEGS', 'uint32'), ('IFLA_GSO_MAX_SIZE', '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): # # 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: if '/' in self.value: netns_path = self.value else: netns_path = '%s/%s' % (self.netns_run_dir, self.value) self.netns_fd = os.open(netns_path, os.O_RDONLY) self['value'] = self.netns_fd self.register_clean_cb(self.close) nla.encode(self) 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')) @staticmethod def protinfo(self, *argv, **kwarg): proto_map = {AF_BRIDGE: protinfo_bridge} return proto_map.get(self['family'], self.hex) 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_slave_data')) @staticmethod def info_slave_data(self, *argv, **kwarg): ''' Return IFLA_INFO_SLAVE_DATA type based on IFLA_INFO_SLAVE_KIND. ''' kind = self.get_attr('IFLA_INFO_SLAVE_KIND') data_map = {'bridge': self.bridge_slave_data, 'bond': self.bond_slave_data} return data_map.get(kind, self.hex) class bridge_slave_data(protinfo_bridge): pass class bond_slave_data(nla): nla_map = (('IFLA_BOND_SLAVE_UNSPEC', 'none'), ('IFLA_BOND_SLAVE_STATE', 'uint8'), ('IFLA_BOND_SLAVE_MII_STATUS', 'uint8'), ('IFLA_BOND_SLAVE_LINK_FAILURE_COUNT', 'uint32'), ('IFLA_BOND_SLAVE_PERM_HWADDR', 'l2addr'), ('IFLA_BOND_SLAVE_QUEUE_ID', 'uint16'), ('IFLA_BOND_SLAVE_AD_AGGREGATOR_ID', 'uint16')) @staticmethod 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') return self.data_map.get(kind, self.hex) class veth_data(nla): nla_map = (('VETH_INFO_UNSPEC', 'none'), ('VETH_INFO_PEER', 'info_peer')) @staticmethod def info_peer(self, *argv, **kwarg): return ifinfveth class gre_data(nla): nla_map = (('IFLA_GRE_UNSPEC', 'none'), ('IFLA_GRE_LINK', 'uint32'), ('IFLA_GRE_IFLAGS', 'uint16'), ('IFLA_GRE_OFLAGS', 'uint16'), ('IFLA_GRE_IKEY', 'be32'), ('IFLA_GRE_OKEY', 'be32'), ('IFLA_GRE_LOCAL', 'ip4addr'), ('IFLA_GRE_REMOTE', 'ip4addr'), ('IFLA_GRE_TTL', 'uint8'), ('IFLA_GRE_TOS', 'uint8'), ('IFLA_GRE_PMTUDISC', 'uint8'), ('IFLA_GRE_ENCAP_LIMIT', 'uint8'), ('IFLA_GRE_FLOWINFO', 'be32'), ('IFLA_GRE_FLAGS', 'uint32'), ('IFLA_GRE_ENCAP_TYPE', 'uint16'), ('IFLA_GRE_ENCAP_FLAGS', 'uint16'), ('IFLA_GRE_ENCAP_SPORT', 'be16'), ('IFLA_GRE_ENCAP_DPORT', 'be16'), ('IFLA_GRE_COLLECT_METADATA', 'flag'), ('IFLA_GRE_IGNORE_DF', 'uint8'), ('IFLA_GRE_FWMARK', 'uint32')) class ip6gre_data(nla): # Ostensibly the same as ip6gre_data except that local # and remote are ipv6 addrs. # As of Linux 4.8,IFLA_GRE_COLLECT_METADATA has not been # implemented for IPv6. # Linux uses the same enum names for v6 and v4 (in if_tunnel.h); # Here we name them IFLA_IP6GRE_xxx instead to avoid conflicts # with gre_data above. nla_map = (('IFLA_IP6GRE_UNSPEC', 'none'), ('IFLA_IP6GRE_LINK', 'uint32'), ('IFLA_IP6GRE_IFLAGS', 'uint16'), ('IFLA_IP6GRE_OFLAGS', 'uint16'), ('IFLA_IP6GRE_IKEY', 'be32'), ('IFLA_IP6GRE_OKEY', 'be32'), ('IFLA_IP6GRE_LOCAL', 'ip6addr'), ('IFLA_IP6GRE_REMOTE', 'ip6addr'), ('IFLA_IP6GRE_TTL', 'uint8'), ('IFLA_IP6GRE_TOS', 'uint8'), ('IFLA_IP6GRE_PMTUDISC', 'uint8'), ('IFLA_IP6GRE_ENCAP_LIMIT', 'uint8'), ('IFLA_IP6GRE_FLOWINFO', 'be32'), ('IFLA_IP6GRE_FLAGS', 'uint32'), ('IFLA_IP6GRE_ENCAP_TYPE', 'uint16'), ('IFLA_IP6GRE_ENCAP_FLAGS', 'uint16'), ('IFLA_IP6GRE_ENCAP_SPORT', 'be16'), ('IFLA_IP6GRE_ENCAP_DPORT', 'be16')) class macvlan_data(macvx_data): pass class macvtap_data(macvx_data): nla_map = [(x[0].replace('MACVLAN', 'MACVTAP'), x[1]) for x in macvx_data.nla_map] class bridge_data(nla): prefix = 'IFLA_' nla_map = (('IFLA_BR_UNSPEC', 'none'), ('IFLA_BR_FORWARD_DELAY', 'uint32'), ('IFLA_BR_HELLO_TIME', 'uint32'), ('IFLA_BR_MAX_AGE', 'uint32'), ('IFLA_BR_AGEING_TIME', 'uint32'), ('IFLA_BR_STP_STATE', 'uint32'), ('IFLA_BR_PRIORITY', 'uint16'), ('IFLA_BR_VLAN_FILTERING', 'uint8'), ('IFLA_BR_VLAN_PROTOCOL', 'be16'), ('IFLA_BR_GROUP_FWD_MASK', 'uint16'), ('IFLA_BR_ROOT_ID', 'br_id'), ('IFLA_BR_BRIDGE_ID', 'br_id'), ('IFLA_BR_ROOT_PORT', 'uint16'), ('IFLA_BR_ROOT_PATH_COST', 'uint32'), ('IFLA_BR_TOPOLOGY_CHANGE', 'uint8'), ('IFLA_BR_TOPOLOGY_CHANGE_DETECTED', 'uint8'), ('IFLA_BR_HELLO_TIMER', 'uint64'), ('IFLA_BR_TCN_TIMER', 'uint64'), ('IFLA_BR_TOPOLOGY_CHANGE_TIMER', 'uint64'), ('IFLA_BR_GC_TIMER', 'uint64'), ('IFLA_BR_GROUP_ADDR', 'l2addr'), ('IFLA_BR_FDB_FLUSH', 'flag'), ('IFLA_BR_MCAST_ROUTER', 'uint8'), ('IFLA_BR_MCAST_SNOOPING', 'uint8'), ('IFLA_BR_MCAST_QUERY_USE_IFADDR', 'uint8'), ('IFLA_BR_MCAST_QUERIER', 'uint8'), ('IFLA_BR_MCAST_HASH_ELASTICITY', 'uint32'), ('IFLA_BR_MCAST_HASH_MAX', 'uint32'), ('IFLA_BR_MCAST_LAST_MEMBER_CNT', 'uint32'), ('IFLA_BR_MCAST_STARTUP_QUERY_CNT', 'uint32'), ('IFLA_BR_MCAST_LAST_MEMBER_INTVL', 'uint64'), ('IFLA_BR_MCAST_MEMBERSHIP_INTVL', 'uint64'), ('IFLA_BR_MCAST_QUERIER_INTVL', 'uint64'), ('IFLA_BR_MCAST_QUERY_INTVL', 'uint64'), ('IFLA_BR_MCAST_QUERY_RESPONSE_INTVL', 'uint64'), ('IFLA_BR_MCAST_STARTUP_QUERY_INTVL', 'uint64'), ('IFLA_BR_NF_CALL_IPTABLES', 'uint8'), ('IFLA_BR_NF_CALL_IP6TABLES', 'uint8'), ('IFLA_BR_NF_CALL_ARPTABLES', 'uint8'), ('IFLA_BR_VLAN_DEFAULT_PVID', 'uint16'), ('IFLA_BR_PAD', 'uint64'), ('IFLA_BR_VLAN_STATS_ENABLED', 'uint8'), ('IFLA_BR_MCAST_STATS_ENABLED', 'uint8'), ('IFLA_BR_MCAST_IGMP_VERSION', 'uint8'), ('IFLA_BR_MCAST_MLD_VERSION', 'uint8')) class br_id(ifla_bridge_id): pass # IFLA_INFO_DATA plugin system prototype data_map = {'macvlan': macvlan_data, 'macvtap': macvtap_data, 'gre': gre_data, 'gretap': gre_data, 'ip6gre': ip6gre_data, 'ip6gretap': ip6gre_data, 'veth': veth_data, 'bridge': bridge_data} # expand supported interface types data_map.update(data_plugins) @staticmethod def af_spec(self, *argv, **kwarg): specs = {0: self.af_spec_inet, AF_INET: self.af_spec_inet, AF_INET6: self.af_spec_inet, AF_BRIDGE: self.af_spec_bridge} return specs.get(self['family'], self.hex) class af_spec_bridge(nla): prefix = 'IFLA_BRIDGE_' # Bug-Url: https://github.com/svinota/pyroute2/issues/284 # resolve conflict with link()/flags # IFLA_BRIDGE_FLAGS is for compatibility, in nla dicts # IFLA_BRIDGE_VLAN_FLAGS overrides it nla_map = ((0, 'IFLA_BRIDGE_FLAGS', 'uint16'), (0, 'IFLA_BRIDGE_VLAN_FLAGS', 'vlan_flags'), (1, 'IFLA_BRIDGE_MODE', 'uint16'), (2, 'IFLA_BRIDGE_VLAN_INFO', 'vlan_info')) class vlan_flags(nla): fields = [('value', 'H')] def encode(self): # convert flags if isinstance(self['value'], basestring): self['value'] = BRIDGE_FLAGS_NAMES['BRIDGE_FLAGS_' + self['value'].upper()] nla.encode(self) class vlan_info(nla): fields = (('flags', 'H'), ('vid', 'H')) @staticmethod def flags2names(flags): ret = [] for flag in BRIDGE_VLAN_VALUES: if (flag & flags) == flag: ret.append(BRIDGE_VLAN_VALUES[flag]) return ret @staticmethod def names2flags(flags): ret = 0 for flag in flags: ret |= BRIDGE_VLAN_NAMES['BRIDGE_VLAN_INFO_' + flag.upper()] return ret def encode(self): # convert flags if isinstance(self['flags'], (set, tuple, list)): self['flags'] = self.names2flags(self['flags']) return super(nla, self).encode() class af_spec_inet(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 # ./include/uapi/linux/ip.h field_names = ('dummy', 'forwarding', 'mc_forwarding', 'proxy_arp', 'accept_redirects', 'secure_redirects', 'send_redirects', 'shared_media', 'rp_filter', 'accept_source_route', 'bootp_relay', 'log_martians', 'tag', 'arpfilter', 'medium_id', 'noxfrm', 'nopolicy', 'force_igmp_version', 'arp_announce', 'arp_ignore', 'promote_secondaries', 'arp_accept', 'arp_notify', 'accept_local', 'src_vmark', 'proxy_arp_pvlan', 'route_localnet', 'igmpv2_unsolicited_report_interval', 'igmpv3_unsolicited_report_interval') 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): # ./include/uapi/linux/snmp.h field_names = ('num', 'inpkts', 'inoctets', 'indelivers', 'outforwdatagrams', 'outpkts', 'outoctets', 'inhdrerrors', 'intoobigerrors', 'innoroutes', 'inaddrerrors', 'inunknownprotos', 'intruncatedpkts', 'indiscards', 'outdiscards', 'outnoroutes', 'reasmtimeout', 'reasmreqds', 'reasmoks', 'reasmfails', 'fragoks', 'fragfails', 'fragcreates', 'inmcastpkts', 'outmcastpkts', 'inbcastpkts', 'outbcastpkts', 'inmcastoctets', 'outmcastoctets', 'inbcastoctets', 'outbcastoctets', 'csumerrors', 'noectpkts', 'ect1pkts', 'ect0pkts', 'cepkts') fields = [(i, 'Q') for i in field_names] class icmp6_stats(nla): # ./include/uapi/linux/snmp.h field_names = ('num', 'inmsgs', 'inerrors', 'outmsgs', 'outerrors', 'csumerrors') fields = [(i, 'Q') for i in field_names] class ifinfmsg(ifinfbase, nlmsg): pass class ifinfveth(ifinfbase, nla): pass pyroute2-0.4.21/pyroute2/netlink/rtnl/ifinfmsg/compat.py0000664000175000017500000003555613152305337023200 0ustar peetpeet00000000000000import os import json import errno import select import struct import threading import subprocess from fcntl import ioctl from pyroute2 import config from pyroute2.common import map_enoent from pyroute2.netlink.rtnl import RTM_VALUES from pyroute2.netlink.rtnl.marshal import MarshalRtnl from pyroute2.netlink.rtnl.ifinfmsg import ifinfmsg from pyroute2.netlink.exceptions import NetlinkError from pyroute2.netlink.rtnl.riprsocket import RawIPRSocket # 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 # _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' if config.machine in ('i386', 'i686', 'x86_64'): TUNSETIFF = 0x400454ca TUNSETPERSIST = 0x400454cb TUNSETOWNER = 0x400454cc TUNSETGROUP = 0x400454ce elif config.machine in ('ppc64', 'mips'): 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 def compat_fix_attrs(msg, nl): kind = None ifname = msg.get_attr('IFLA_IFNAME') # fix master if not nl.capabilities['provide_master']: 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]) elif 'attrs' in msg: kind = get_interface_type(ifname) msg['attrs'].append(['IFLA_LINKINFO', {'attrs': [['IFLA_INFO_KIND', kind]]}]) else: return 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}]) def proxy_linkinfo(data, nl): marshal = MarshalRtnl() inbox = marshal.parse(data) data = b'' for msg in inbox: if msg['event'] == 'NLMSG_ERROR': data += msg.data continue # Sysfs operations can require root permissions, # but the script can be run under a normal user # Bug-Url: https://github.com/svinota/pyroute2/issues/113 try: compat_fix_attrs(msg, nl) except OSError: # We can safely ignore here any OSError. # In the worst case, we just return what we have got # from the kernel via netlink pass msg.reset() msg.encode() data += msg.data return {'verdict': 'forward', 'data': data} def proxy_setlink(imsg, nl): def get_interface(index): msg = nl.get_links(index)[0] try: kind = msg.get_attr('IFLA_LINKINFO').get_attr('IFLA_INFO_KIND') except AttributeError: kind = 'unknown' return {'ifname': msg.get_attr('IFLA_IFNAME'), 'master': msg.get_attr('IFLA_MASTER'), 'kind': kind} msg = ifinfmsg(imsg.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') and infodata is not None: 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. manage the port forward_map = {'team': manage_team_port, 'bridge': compat_bridge_port, 'bond': compat_bond_port} if master['kind'] in forward_map: func = forward_map[master['kind']] forward = func(cmd, master['ifname'], ifname, nl) if forward is not None: return {'verdict': 'forward', 'data': imsg.data} def sync(f): ''' A decorator to wrap up external utility calls. A decorated function receives a netlink message as a parameter, and then: 1. Starts a monitoring thread 2. Performs the external call 3. Waits for a netlink event specified by `msg` 4. Joins the monitoring thread If the wrapped function raises an exception, the monitoring thread will be forced to stop via the control channel pipe. The exception will be then forwarded. ''' def monitor(event, ifname, cmd): with RawIPRSocket() as ipr: poll = select.poll() poll.register(ipr, select.POLLIN | select.POLLPRI) poll.register(cmd, select.POLLIN | select.POLLPRI) ipr.bind() while True: events = poll.poll() for (fd, event) in events: if fd == ipr.fileno(): msgs = ipr.get() for msg in msgs: if msg.get('event') == event and \ msg.get_attr('IFLA_IFNAME') == ifname: return else: return def decorated(msg): rcmd, cmd = os.pipe() t = threading.Thread(target=monitor, args=(RTM_VALUES[msg['header']['type']], msg.get_attr('IFLA_IFNAME'), rcmd)) t.start() ret = None try: ret = f(msg) except Exception: raise finally: os.write(cmd, b'q') t.join() os.close(rcmd) os.close(cmd) return ret return decorated def proxy_dellink(imsg, nl): orig_msg = ifinfmsg(imsg.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') # team interfaces can be stopped by a normal RTM_DELLINK if kind == 'bond' and not nl.capabilities['create_bond']: return compat_del_bond(msg) elif kind == 'bridge' and not nl.capabilities['create_bridge']: return compat_del_bridge(msg) return {'verdict': 'forward', 'data': imsg.data} def proxy_newlink(imsg, nl): msg = ifinfmsg(imsg.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 == 'bond' and not nl.capabilities['create_bond']: return compat_create_bond(msg) elif kind == 'bridge' and not nl.capabilities['create_bridge']: return compat_create_bridge(msg) return {'verdict': 'forward', 'data': imsg.data} @map_enoent @sync def manage_team(msg): if msg['header']['type'] != RTM_NEWLINK: raise ValueError('wrong command type') 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) @map_enoent def manage_team_port(cmd, master, ifname, nl): with open(os.devnull, 'w') as fnull: subprocess.check_call(['teamdctl', master, 'port', 'remove' if cmd == 'del' else 'add', ifname], stdout=fnull, stderr=fnull) @sync def manage_tuntap(msg): if TUNSETIFF is None: raise NetlinkError(errno.EOPNOTSUPP, 'Arch not supported') if msg['header']['type'] != RTM_NEWLINK: raise NetlinkError(errno.EOPNOTSUPP, '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) @sync def compat_create_bridge(msg): name = msg.get_attr('IFLA_IFNAME') with open(os.devnull, 'w') as fnull: subprocess.check_call(['brctl', 'addbr', name], stdout=fnull, stderr=fnull) @sync def compat_create_bond(msg): name = msg.get_attr('IFLA_IFNAME') 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) @sync def compat_del_bridge(msg): name = msg.get_attr('IFLA_IFNAME') 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) @sync def compat_del_bond(msg): name = msg.get_attr('IFLA_IFNAME') 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, nl): if nl.capabilities['create_bridge']: 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, nl): if nl.capabilities['create_bond']: 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: try: f = open(i % (name)) except UnicodeEncodeError: # a special case with python3 on Ubuntu 14 f = open(i % (name.encode('utf-8'))) 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.4.21/pyroute2/netlink/rtnl/ifinfmsg/proxy.py0000664000175000017500000001605213152305337023064 0ustar peetpeet00000000000000import os import json import errno import select import struct import threading import subprocess from fcntl import ioctl from pyroute2 import config from pyroute2.common import map_enoent from pyroute2.netlink.rtnl.ifinfmsg import IFT_TUN from pyroute2.netlink.rtnl.ifinfmsg import IFT_TAP from pyroute2.netlink.rtnl.ifinfmsg import IFT_NO_PI from pyroute2.netlink.rtnl.ifinfmsg import IFT_ONE_QUEUE from pyroute2.netlink.rtnl.ifinfmsg import IFT_VNET_HDR from pyroute2.netlink.rtnl.ifinfmsg import IFT_MULTI_QUEUE from pyroute2.netlink.rtnl.ifinfmsg import RTM_NEWLINK from pyroute2.netlink.rtnl import RTM_VALUES from pyroute2.netlink.rtnl.riprsocket import RawIPRSocket from pyroute2.netlink.exceptions import NetlinkError _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' if config.machine in ('i386', 'i686', 'x86_64', 'armv6l', 'armv7l'): TUNSETIFF = 0x400454ca TUNSETPERSIST = 0x400454cb TUNSETOWNER = 0x400454cc TUNSETGROUP = 0x400454ce elif config.machine in ('ppc64', 'mips'): TUNSETIFF = 0x800454ca TUNSETPERSIST = 0x800454cb TUNSETOWNER = 0x800454cc TUNSETGROUP = 0x800454ce else: TUNSETIFF = None def sync(f): ''' A decorator to wrap up external utility calls. A decorated function receives a netlink message as a parameter, and then: 1. Starts a monitoring thread 2. Performs the external call 3. Waits for a netlink event specified by `msg` 4. Joins the monitoring thread If the wrapped function raises an exception, the monitoring thread will be forced to stop via the control channel pipe. The exception will be then forwarded. ''' def monitor(event, ifname, cmd): with RawIPRSocket() as ipr: poll = select.poll() poll.register(ipr, select.POLLIN | select.POLLPRI) poll.register(cmd, select.POLLIN | select.POLLPRI) ipr.bind() while True: events = poll.poll() for (fd, event) in events: if fd == ipr.fileno(): msgs = ipr.get() for msg in msgs: if msg.get('event') == event and \ msg.get_attr('IFLA_IFNAME') == ifname: return else: return def decorated(msg): rcmd, cmd = os.pipe() t = threading.Thread(target=monitor, args=(RTM_VALUES[msg['header']['type']], msg.get_attr('IFLA_IFNAME'), rcmd)) t.start() ret = None try: ret = f(msg) except Exception: raise finally: os.write(cmd, b'q') t.join() return ret return decorated def proxy_setlink(msg, nl): def get_interface(index): msg = nl.get_links(index)[0] try: kind = msg.get_attr('IFLA_LINKINFO').get_attr('IFLA_INFO_KIND') except AttributeError: kind = 'unknown' return {'ifname': msg.get_attr('IFLA_IFNAME'), 'master': msg.get_attr('IFLA_MASTER'), 'kind': kind} forward = True # 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' ifname = msg.get_attr('IFLA_IFNAME') or \ get_interface(msg['index'])['ifname'] # 2. manage the port forward_map = {'team': manage_team_port} if master['kind'] in forward_map: func = forward_map[master['kind']] forward = func(cmd, master['ifname'], ifname, nl) if forward is not None: return {'verdict': 'forward', 'data': msg.data} def proxy_newlink(msg, nl): 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) return {'verdict': 'forward', 'data': msg.data} @map_enoent @sync def manage_team(msg): if msg['header']['type'] != RTM_NEWLINK: raise ValueError('wrong command type') 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) @map_enoent def manage_team_port(cmd, master, ifname, nl): with open(os.devnull, 'w') as fnull: subprocess.check_call(['teamdctl', master, 'port', 'remove' if cmd == 'del' else 'add', ifname], stdout=fnull, stderr=fnull) @sync def manage_tuntap(msg): if TUNSETIFF is None: raise NetlinkError(errno.EOPNOTSUPP, 'Arch not supported') if msg['header']['type'] != RTM_NEWLINK: raise NetlinkError(errno.EOPNOTSUPP, '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) pyroute2-0.4.21/pyroute2/netlink/rtnl/ndtmsg.py0000664000175000017500000000456513127143031021374 0ustar peetpeet00000000000000 from pyroute2.netlink import nlmsg from pyroute2.netlink import nla class ndtmsg(nlmsg): ''' Neighbour table message ''' __slots__ = () fields = (('family', 'B'), ('__pad', '3x')) nla_map = (('NDTA_UNSPEC', 'none'), ('NDTA_NAME', 'asciiz'), ('NDTA_THRESH1', 'uint32'), ('NDTA_THRESH2', 'uint32'), ('NDTA_THRESH3', 'uint32'), ('NDTA_CONFIG', 'config'), ('NDTA_PARMS', 'parms'), ('NDTA_STATS', 'stats'), ('NDTA_GC_INTERVAL', 'uint64')) class config(nla): __slots__ = () fields = (('key_len', 'H'), ('entry_size', 'H'), ('entries', 'I'), ('last_flush', 'I'), # delta to now in msecs ('last_rand', 'I'), # delta to now in msecs ('hash_rnd', 'I'), ('hash_mask', 'I'), ('hash_chain_gc', 'I'), ('proxy_qlen', 'I')) class stats(nla): __slots__ = () fields = (('allocs', 'Q'), ('destroys', 'Q'), ('hash_grows', 'Q'), ('res_failed', 'Q'), ('lookups', 'Q'), ('hits', 'Q'), ('rcv_probes_mcast', 'Q'), ('rcv_probes_ucast', 'Q'), ('periodic_gc_runs', 'Q'), ('forced_gc_runs', 'Q')) class parms(nla): __slots__ = () nla_map = (('NDTPA_UNSPEC', 'none'), ('NDTPA_IFINDEX', 'uint32'), ('NDTPA_REFCNT', 'uint32'), ('NDTPA_REACHABLE_TIME', 'uint64'), ('NDTPA_BASE_REACHABLE_TIME', 'uint64'), ('NDTPA_RETRANS_TIME', 'uint64'), ('NDTPA_GC_STALETIME', 'uint64'), ('NDTPA_DELAY_PROBE_TIME', 'uint64'), ('NDTPA_QUEUE_LEN', 'uint32'), ('NDTPA_APP_PROBES', 'uint32'), ('NDTPA_UCAST_PROBES', 'uint32'), ('NDTPA_MCAST_PROBES', 'uint32'), ('NDTPA_ANYCAST_DELAY', 'uint64'), ('NDTPA_PROXY_DELAY', 'uint64'), ('NDTPA_PROXY_QLEN', 'uint32'), ('NDTPA_LOCKTIME', 'uint64'), ('NDTPA_QUEUE_LENBYTES', 'uint32')) pyroute2-0.4.21/pyroute2/netlink/rtnl/marshal.py0000664000175000017500000000343713123443516021533 0ustar peetpeet00000000000000from pyroute2.netlink import rtnl from pyroute2.netlink.nlsocket import Marshal 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.ndtmsg import ndtmsg from pyroute2.netlink.rtnl.fibmsg import fibmsg from pyroute2.netlink.rtnl.ifinfmsg import ifinfmsg from pyroute2.netlink.rtnl.ifaddrmsg import ifaddrmsg class MarshalRtnl(Marshal): msg_map = {rtnl.RTM_NEWLINK: ifinfmsg, rtnl.RTM_DELLINK: ifinfmsg, rtnl.RTM_GETLINK: ifinfmsg, rtnl.RTM_SETLINK: ifinfmsg, rtnl.RTM_NEWADDR: ifaddrmsg, rtnl.RTM_DELADDR: ifaddrmsg, rtnl.RTM_GETADDR: ifaddrmsg, rtnl.RTM_NEWROUTE: rtmsg, rtnl.RTM_DELROUTE: rtmsg, rtnl.RTM_GETROUTE: rtmsg, rtnl.RTM_NEWRULE: fibmsg, rtnl.RTM_DELRULE: fibmsg, rtnl.RTM_GETRULE: fibmsg, rtnl.RTM_NEWNEIGH: ndmsg, rtnl.RTM_DELNEIGH: ndmsg, rtnl.RTM_GETNEIGH: ndmsg, rtnl.RTM_NEWQDISC: tcmsg, rtnl.RTM_DELQDISC: tcmsg, rtnl.RTM_GETQDISC: tcmsg, rtnl.RTM_NEWTCLASS: tcmsg, rtnl.RTM_DELTCLASS: tcmsg, rtnl.RTM_GETTCLASS: tcmsg, rtnl.RTM_NEWTFILTER: tcmsg, rtnl.RTM_DELTFILTER: tcmsg, rtnl.RTM_GETTFILTER: tcmsg, rtnl.RTM_NEWNEIGHTBL: ndtmsg, rtnl.RTM_GETNEIGHTBL: ndtmsg, rtnl.RTM_SETNEIGHTBL: ndtmsg} def fix_message(self, msg): # FIXME: pls do something with it try: msg['event'] = rtnl.RTM_VALUES[msg['header']['type']] except: pass pyroute2-0.4.21/pyroute2/netlink/rtnl/riprsocket.py0000664000175000017500000000106013152305337022257 0ustar peetpeet00000000000000from pyroute2.netlink import rtnl from pyroute2.netlink import NETLINK_ROUTE from pyroute2.netlink.nlsocket import NetlinkSocket from pyroute2.netlink.rtnl.marshal import MarshalRtnl class RawIPRSocketMixin(object): def __init__(self, fileno=None): super(RawIPRSocketMixin, self).__init__(NETLINK_ROUTE, fileno=fileno) self.marshal = MarshalRtnl() def bind(self, groups=rtnl.RTNL_GROUPS, async=False): super(RawIPRSocketMixin, self).bind(groups, async=async) class RawIPRSocket(RawIPRSocketMixin, NetlinkSocket): pass pyroute2-0.4.21/pyroute2/netlink/ipq/0000775000175000017500000000000013152333366017340 5ustar peetpeet00000000000000pyroute2-0.4.21/pyroute2/netlink/ipq/__init__.py0000664000175000017500000000657613123443516021464 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.data, (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.4.21/pyroute2/netlink/nlsocket.py0000664000175000017500000010316613152305337020747 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 sys import time import select import struct import logging import traceback import threading 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 pyroute2 import config from pyroute2.config import AF_NETLINK 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 NLMSG_ERROR from pyroute2.netlink import NLMSG_DONE from pyroute2.netlink import NETLINK_GENERIC from pyroute2.netlink import NETLINK_LISTEN_ALL_NSID from pyroute2.netlink import NLM_F_DUMP from pyroute2.netlink import NLM_F_MULTI from pyroute2.netlink import NLM_F_REQUEST from pyroute2.netlink import SOL_NETLINK from pyroute2.netlink.exceptions import NetlinkError from pyroute2.netlink.exceptions import NetlinkDecodeError from pyroute2.netlink.exceptions import NetlinkHeaderDecodeError try: from Queue import Queue except ImportError: from queue import Queue log = logging.getLogger(__name__) class Marshal(object): ''' Generic marshalling class ''' msg_map = {} type_offset = 4 type_format = 'H' 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, seq=None, callback=None): ''' 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 = [] # there must be at least one header in the buffer, # 'IHHII' == 16 bytes while offset <= len(data) - 16: # pick type and length length, = struct.unpack_from('I', data, offset) if length == 0: break error = None msg_type, = struct.unpack_from(self.type_format, data, offset + self.type_offset) if msg_type == NLMSG_ERROR: code = abs(struct.unpack_from('i', data, offset+16)[0]) if code > 0: error = NetlinkError(code) msg_class = self.msg_map.get(msg_type, nlmsg) msg = msg_class(data, offset=offset) try: msg.decode() msg['header']['error'] = error # try to decode encapsulated error message if error is not None: enc_type = struct.unpack_from('H', data, offset+24)[0] enc_class = self.msg_map.get(enc_type, nlmsg) enc = enc_class(data, offset=offset+20) enc.decode() msg['header']['errmsg'] = enc if callback and seq == msg['header']['sequence_number']: if callback(msg): offset += msg.length continue 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 modify the 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, fileno=None, all_ns=False): # # That's a trick. Python 2 is not able to construct # sockets from an open FD. # # So raise an exception, if the major version is < 3 # and fileno is not None. # # Do NOT use fileno in a core pyroute2 functionality, # since the core should be both Python 2 and 3 # compatible. # super(NetlinkMixin, self).__init__() if fileno is not None and sys.version_info[0] < 3: raise NotImplementedError('fileno parameter is not supported ' 'on Python < 3.2') # 8<----------------------------------------- self.addr_pool = AddrPool(minaddr=0x000000ff, maxaddr=0x0000ffff) self.epid = None self.port = 0 self.fixed = True self.family = family self._fileno = fileno self.backlog = {0: []} self.callbacks = [] # [(predicate, callback, args), ...] self.pthread = None self.closed = False self.capabilities = {'create_bridge': config.kernel > [3, 2, 0], 'create_bond': config.kernel > [3, 2, 0], 'create_dummy': True, 'provide_master': config.kernel[0] > 2} self.backlog_lock = threading.Lock() self.read_lock = threading.Lock() self.change_master = threading.Event() self.lock = LockFactory() self._sock = None self._ctrl_read, self._ctrl_write = os.pipe() self.buffer_queue = Queue() self.qsize = 0 self.log = [] self.get_timeout = 30 self.get_timeout_exception = None self.all_ns = all_ns 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 defaults self.post_init() def clone(self): return type(self)(family=self.family) def close(self): try: os.close(self._ctrl_write) os.close(self._ctrl_read) except OSError: # ignore the case when it is closed already pass def __enter__(self): return self def __exit__(self, exc_type, exc_value, traceback): self.close() def release(self): log.warning("The `release()` call is deprecated") log.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 sendto(self, *argv, **kwarg): return self._sendto(*argv, **kwarg) def recv(self, *argv, **kwarg): return self._recv(*argv, **kwarg) def recv_into(self, *argv, **kwarg): return self._recv_into(*argv, **kwarg) def recv_ft(self, size=None): return self._recv(size) def async_recv(self): poll = select.poll() poll.register(self._sock, select.POLLIN | select.POLLPRI) poll.register(self._ctrl_read, select.POLLIN | select.POLLPRI) sockfd = self._sock.fileno() while True: events = poll.poll() for (fd, event) in events: if fd == sockfd: try: data = bytearray(64000) self._sock.recv_into(data, 64000) self.buffer_queue.put(data) except Exception as e: self.buffer_queue.put(e) else: return 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 = self.epid or os.getpid() msg['header']['type'] = msg_type msg['header']['flags'] = msg_flags msg['header']['sequence_number'] = msg_seq msg['header']['pid'] = msg_pid self.sendto_gate(msg, addr) except: raise finally: if msg_seq != 0: self.lock[msg_seq].release() def sendto_gate(self, msg, addr): raise NotImplementedError() def get(self, bufsize=DEFAULT_RCVBUF, msg_seq=0, terminate=None, callback=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_ft(bufsize) # Parse data msgs = self.marshal.parse(data, msg_seq, callback) # 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.01, float(current) / 60000)) message = ("Packet burst: " "delta=%s qsize=%s delay=%s" % (delta, current, delay)) if delay < 1: log.debug(message) else: log.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: log.warning("Callback fail: %s" % (cr)) log.warning(traceback.format_exc()) # 8<----------------------------------------------- self.backlog[seq].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, callback=None, exception_catch=Exception, exception_handler=None): def do_try(): 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, callback=callback) return ret except Exception: 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) while True: try: return do_try() except exception_catch as e: if exception_handler and not exception_handler(e): continue raise except Exception: raise class BatchAddrPool(object): def alloc(self, *argv, **kwarg): return 0 def free(self, *argv, **kwarg): pass class BatchBacklogQueue(list): def append(self, *argv, **kwarg): pass def pop(self, *argv, **kwarg): pass class BatchBacklog(dict): def __getitem__(self, key): return BatchBacklogQueue() def __setitem__(self, key, value): pass def __delitem__(self, key): pass class BatchSocket(NetlinkMixin): def post_init(self): self.backlog = BatchBacklog() self.addr_pool = BatchAddrPool() self._sock = None self.reset() def reset(self): self.batch = bytearray() def sendto_gate(self, msg, addr): msg.data = self.batch msg.offset = len(self.batch) msg.encode() def get(self, *argv, **kwarg): pass class NetlinkSocket(NetlinkMixin): def post_init(self): # recreate the underlying socket with self.lock: if self._sock is not None: self._sock.close() self._sock = config.SocketBase(AF_NETLINK, SOCK_DGRAM, self.family, self._fileno) for name in ('getsockname', 'getsockopt', 'makefile', 'setsockopt', 'setblocking', 'settimeout', 'gettimeout', 'shutdown', 'recvfrom', 'recvfrom_into', 'fileno'): setattr(self, name, getattr(self._sock, name)) self._sendto = getattr(self._sock, 'sendto') self._recv = getattr(self._sock, 'recv') self._recv_into = getattr(self._sock, 'recv_into') # setup fast-track self.recv_ft = getattr(self._sock, 'recv') self.sendto_gate = self._gate # monkey patch recv_into on Python 2.6 if sys.version_info[0] == 2 and sys.version_info[1] < 7: # --> monkey patch the socket log.warning('patching socket.recv_into()') def patch(data, bsize): data[0:] = self._sock.recv(bsize) self._sock.recv_into = patch self.setsockopt(SOL_SOCKET, SO_SNDBUF, 1024 * 1024) self.setsockopt(SOL_SOCKET, SO_RCVBUF, 1024 * 1024) if self.all_ns: self.setsockopt(SOL_NETLINK, NETLINK_LISTEN_ALL_NSID, 1) def _gate(self, msg, addr): msg.reset() msg.encode() return self._sock.sendto(msg.data, addr) 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) self._sock.bind((self.epid, self.groups)) else: for port in range(1024): try: self.port = port self.epid = self.pid + (self.port << 22) self._sock.bind((self.epid, self.groups)) break except Exception: # create a new underlying socket -- on kernel 4 # one failed bind() makes the socket useless self.post_init() else: raise KeyError('no free address available') # all is OK till now, so start async recv, if we need if async: def recv_plugin(*argv, **kwarg): data_in = self.buffer_queue.get() if isinstance(data_in, Exception): raise data_in else: return data_in def recv_into_plugin(data, *argv, **kwarg): data_in = self.buffer_queue.get() if isinstance(data_in, Exception): raise data_in else: data[:] = data_in return len(data_in) self._recv = recv_plugin self._recv_into = recv_into_plugin self.recv_ft = recv_plugin self.pthread = threading.Thread(name="Netlink async cache", target=self.async_recv) self.pthread.setDaemon(True) self.pthread.start() def close(self): ''' Correctly close the socket and free all resources. ''' with self.lock: if self.closed: return self.closed = True if self.pthread: os.write(self._ctrl_write, b'exit') self.pthread.join() super(NetlinkSocket, self).close() # Common shutdown procedure self._sock.close() pyroute2-0.4.21/pyroute2/netlink/devlink/0000775000175000017500000000000013152333366020203 5ustar peetpeet00000000000000pyroute2-0.4.21/pyroute2/netlink/devlink/__init__.py0000664000175000017500000001145613123443516022320 0ustar peetpeet00000000000000''' devlink module ============== ''' from pyroute2.common import map_namespace from pyroute2.netlink import genlmsg from pyroute2.netlink.generic import GenericNetlinkSocket from pyroute2.netlink.nlsocket import Marshal # devlink commands DEVLINK_CMD_UNSPEC = 0 DEVLINK_CMD_GET = 1 DEVLINK_CMD_SET = 2 DEVLINK_CMD_NEW = 3 DEVLINK_CMD_DEL = 4 DEVLINK_CMD_PORT_GET = 5 DEVLINK_CMD_PORT_SET = 6 DEVLINK_CMD_PORT_NEW = 7 DEVLINK_CMD_PORT_DEL = 8 DEVLINK_CMD_PORT_SPLIT = 9 DEVLINK_CMD_PORT_UNSPLIT = 10 DEVLINK_CMD_SB_GET = 11 DEVLINK_CMD_SB_SET = 12 DEVLINK_CMD_SB_NEW = 13 DEVLINK_CMD_SB_DEL = 14 DEVLINK_CMD_SB_POOL_GET = 15 DEVLINK_CMD_SB_POOL_SET = 16 DEVLINK_CMD_SB_POOL_NEW = 17 DEVLINK_CMD_SB_POOL_DEL = 18 DEVLINK_CMD_SB_PORT_POOL_GET = 19 DEVLINK_CMD_SB_PORT_POOL_SET = 20 DEVLINK_CMD_SB_PORT_POOL_NEW = 21 DEVLINK_CMD_SB_PORT_POOL_DEL = 22 DEVLINK_CMD_SB_TC_POOL_BIND_GET = 23 DEVLINK_CMD_SB_TC_POOL_BIND_SET = 24 DEVLINK_CMD_SB_TC_POOL_BIND_NEW = 25 DEVLINK_CMD_SB_TC_POOL_BIND_DEL = 26 DEVLINK_CMD_SB_OCC_SNAPSHOT = 27 DEVLINK_CMD_SB_OCC_MAX_CLEAR = 28 DEVLINK_CMD_MAX = DEVLINK_CMD_SB_OCC_MAX_CLEAR (DEVLINK_NAMES, DEVLINK_VALUES) = map_namespace('DEVLINK_CMD_', globals()) # port type DEVLINK_PORT_TYPE_NOTSET = 0 DEVLINK_PORT_TYPE_AUTO = 1 DEVLINK_PORT_TYPE_ETH = 2 DEVLINK_PORT_TYPE_IB = 3 # threshold type DEVLINK_SB_POOL_TYPE_INGRESS = 0 DEVLINK_SB_POOL_TYPE_EGRESS = 1 DEVLINK_SB_THRESHOLD_TO_ALPHA_MAX = 20 class devlinkcmd(genlmsg): prefix = 'DEVLINK_ATTR_' nla_map = (('DEVLINK_ATTR_UNSPEC', 'none'), ('DEVLINK_ATTR_BUS_NAME', 'asciiz'), ('DEVLINK_ATTR_DEV_NAME', 'asciiz'), ('DEVLINK_ATTR_PORT_INDEX', 'uint32'), ('DEVLINK_ATTR_PORT_TYPE', 'uint16'), ('DEVLINK_ATTR_PORT_DESIRED_TYPE', 'uint16'), ('DEVLINK_ATTR_PORT_NETDEV_IFINDEX', 'uint32'), ('DEVLINK_ATTR_PORT_NETDEV_NAME', 'asciiz'), ('DEVLINK_ATTR_PORT_IBDEV_NAME', 'asciiz'), ('DEVLINK_ATTR_PORT_SPLIT_COUNT', 'uint32'), ('DEVLINK_ATTR_PORT_SPLIT_GROUP', 'uint32'), ('DEVLINK_ATTR_SB_INDEX', 'uint32'), ('DEVLINK_ATTR_SB_SIZE', 'uint32'), ('DEVLINK_ATTR_SB_INGRESS_POOL_COUNT', 'uint16'), ('DEVLINK_ATTR_SB_EGRESS_POOL_COUNT', 'uint16'), ('DEVLINK_ATTR_SB_INGRESS_TC_COUNT', 'uint16'), ('DEVLINK_ATTR_SB_EGRESS_TC_COUNT', 'uint16'), ('DEVLINK_ATTR_SB_POOL_INDEX', 'uint16'), ('DEVLINK_ATTR_SB_POOL_TYPE', 'uint8'), ('DEVLINK_ATTR_SB_POOL_SIZE', 'uint32'), ('DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE', 'uint8'), ('DEVLINK_ATTR_SB_THRESHOLD', 'uint32'), ('DEVLINK_ATTR_SB_TC_INDEX', 'uint16'), ('DEVLINK_ATTR_SB_OCC_CUR', 'uint32'), ('DEVLINK_ATTR_SB_OCC_MAX', 'uint32')) class MarshalDevlink(Marshal): msg_map = {DEVLINK_CMD_UNSPEC: devlinkcmd, DEVLINK_CMD_GET: devlinkcmd, DEVLINK_CMD_SET: devlinkcmd, DEVLINK_CMD_NEW: devlinkcmd, DEVLINK_CMD_DEL: devlinkcmd, DEVLINK_CMD_PORT_GET: devlinkcmd, DEVLINK_CMD_PORT_SET: devlinkcmd, DEVLINK_CMD_PORT_NEW: devlinkcmd, DEVLINK_CMD_PORT_DEL: devlinkcmd, DEVLINK_CMD_PORT_SPLIT: devlinkcmd, DEVLINK_CMD_PORT_UNSPLIT: devlinkcmd, DEVLINK_CMD_SB_GET: devlinkcmd, DEVLINK_CMD_SB_SET: devlinkcmd, DEVLINK_CMD_SB_NEW: devlinkcmd, DEVLINK_CMD_SB_DEL: devlinkcmd, DEVLINK_CMD_SB_POOL_GET: devlinkcmd, DEVLINK_CMD_SB_POOL_SET: devlinkcmd, DEVLINK_CMD_SB_POOL_NEW: devlinkcmd, DEVLINK_CMD_SB_POOL_DEL: devlinkcmd, DEVLINK_CMD_SB_PORT_POOL_GET: devlinkcmd, DEVLINK_CMD_SB_PORT_POOL_SET: devlinkcmd, DEVLINK_CMD_SB_PORT_POOL_NEW: devlinkcmd, DEVLINK_CMD_SB_PORT_POOL_DEL: devlinkcmd, DEVLINK_CMD_SB_TC_POOL_BIND_GET: devlinkcmd, DEVLINK_CMD_SB_TC_POOL_BIND_SET: devlinkcmd, DEVLINK_CMD_SB_TC_POOL_BIND_NEW: devlinkcmd, DEVLINK_CMD_SB_TC_POOL_BIND_DEL: devlinkcmd, DEVLINK_CMD_SB_OCC_SNAPSHOT: devlinkcmd, DEVLINK_CMD_SB_OCC_MAX_CLEAR: devlinkcmd} def fix_message(self, msg): try: msg['event'] = DEVLINK_VALUES[msg['cmd']] except Exception: pass class DevlinkSocket(GenericNetlinkSocket): def __init__(self): GenericNetlinkSocket.__init__(self) self.marshal = MarshalDevlink() def bind(self, groups=0, async=False): GenericNetlinkSocket.bind(self, 'devlink', devlinkcmd, groups, None, async) pyroute2-0.4.21/pyroute2/netlink/__init__.py0000664000175000017500000015730613127143031020662 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:: struct ifaddrmsg { __u8 ifa_family; __u8 ifa_prefixlen; __u8 ifa_flags; __u8 ifa_scope; __u32 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` -- ignore this NLA - `flag` -- boolean flag NLA (no payload; NLA exists = True) - `uint8`, `uint16`, `uint32`, `uint64` -- unsigned int - `be8`, `be16`, `be32`, `be64` -- big-endian unsigned int - `ipaddr` -- IP address, IPv4 or IPv6 - `ip4addr` -- only IPv4 address type - `ip6addr` -- only IPv6 address type - `target` -- a univeral target (IPv4, IPv6, MPLS) - `l2addr` -- MAC address - `hex` -- hex dump as a string -- useful for debugging - `cdata` -- a binary data - `string` -- UTF-8 string - `asciiz` -- zero-terminated ASCII string, no decoding - `array` -- array of simple types (uint8, uint16 etc.) 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). array types ~~~~~~~~~~~ There are different array-like NLA types in the kernel, and some of them are covered by pyroute2. An array of simple type elements:: # declaration nla_map = (('NLA_TYPE', 'array(uint8)'), ...) # data layout +======+======+---------------------------- | len | type | uint8 | uint8 | uint 8 | ... +======+======+---------------------------- # decoded {'attrs': [['NLA_TYPE', (2, 3, 4, 5, ...)], ...], ...} An array of NLAs:: # declaration nla_map = (('NLA_TYPE', '*type'), ...) # data layout +=======+=======+-----------------------+-----------------------+-- | len | type* | len | type | payload | len | type | payload | ... +=======+=======+-----------------------+-----------------------+-- # type* -- in that case the type is OR'ed with NLA_F_NESTED # decoded {'attrs': [['NLA_TYPE', [payload, payload, ...]], ...], ...} 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']]}]]} One important detail is that NLA chain is represented as a list of elements `['NLA_TYPE', value]`, not as a dictionary. The reason is that though in the kernel *usually* NLA chain is a dictionary, the netlink protocol by itself doesn't require elements of each type to be unique. In a message there may be several NLA of the same type. encoding and decoding algo ~~~~~~~~~~~~~~~~~~~~~~~~~~ The message encoding works as follows: 1. Reserve space for the message header (if there is) 2. Iterate defined `fields`, encoding values with `struct.pack()` 3. Iterate NLA from the `attrs` field, looking up types in `nla_map` 4. Encode the header Since every NLA is also an `nlmsg` object, there is a recursion. The decoding process is a bit simpler: 1. Decode the header 2. Iterate `fields`, decoding values with `struct.unpack()` 3. Iterate NLA until the message ends If the `fields` attribute is an empty list, the step 2 will be skipped. The step 3 will be skipped in the case of the empty `nla_map`. If both attributes are empty lists, only the header will be encoded/decoded. 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.data, (0, 0)) Please notice, that NLA list *MUST* be mutable. ''' import weakref import traceback import logging import struct import types import sys import io import re from socket import inet_pton from socket import inet_ntop from socket import AF_INET from socket import AF_INET6 from socket import AF_UNSPEC from pyroute2.common import AF_MPLS from pyroute2.common import hexdump from pyroute2.common import basestring from pyroute2.netlink.exceptions import NetlinkError from pyroute2.netlink.exceptions import NetlinkDecodeError from pyroute2.netlink.exceptions import NetlinkNLADecodeError log = logging.getLogger(__name__) # make pep8 happy _ne = NetlinkError # reexport for compatibility _de = NetlinkDecodeError # class NotInitialized(Exception): pass _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_SOCK_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 # NLA flags NLA_F_NESTED = 1 << 15 NLA_F_NET_BYTEORDER = 1 << 14 # 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 SOL_NETLINK = 270 NETLINK_ADD_MEMBERSHIP = 1 NETLINK_DROP_MEMBERSHIP = 2 NETLINK_PKTINFO = 3 NETLINK_BROADCAST_ERROR = 4 NETLINK_NO_ENOBUFS = 5 NETLINK_RX_RING = 6 NETLINK_TX_RING = 7 NETLINK_LISTEN_ALL_NSID = 8 clean_cbs = {} # Cached results for some struct operations. # No cache invalidation required. cache_fmt = {} cache_hdr = {} cache_jit = {} 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 = tuple() header = tuple() pack = None # pack pragma cell_header = None align = 4 nla_map = {} # NLA mapping nla_flags = 0 # NLA flags value_map = {} is_nla = False prefix = None own_parent = False # caches __compiled_nla = False __compiled_ft = False __t_nla_map = None __r_nla_map = None __slots__ = ( "_buf", "data", "offset", "length", "parent", "decoded", "_nla_init", "_nla_array", "_nla_flags", "value", "_ft_decode", "_r_value_map", "__weakref__" ) def msg_align(self, l): return (l + self.align - 1) & ~ (self.align - 1) def __init__(self, data=None, offset=0, length=None, parent=None, init=None): global cache_jit dict.__init__(self) for i in self.fields: self[i[0]] = 0 # FIXME: only for number values self._buf = None self.data = data or bytearray() self.offset = offset self.length = length or 0 if parent is not None: # some structures use parents, some not, # so don't create cycles without need self.parent = parent if self.own_parent else weakref.proxy(parent) else: self.parent = None self.decoded = False self._nla_init = init self._nla_array = False self._nla_flags = self.nla_flags self['attrs'] = [] self['value'] = NotInitialized self.value = NotInitialized # work only on non-empty mappings if self.nla_map and not self.__class__.__compiled_nla: self.compile_nla() # compile fast-track for particular types if id(self.__class__) in cache_jit: self._ft_decode = cache_jit[id(self.__class__)]['ft_decode'] else: self.compile_ft() self._r_value_map = dict([ (x[1], x[0]) for x in self.value_map.items() ]) if self.header: self['header'] = {} @property def buf(self): logging.error('nlmsg.buf is deprecated:\n%s', ''.join(traceback.format_stack())) if self._buf is None: self._buf = io.BytesIO() self._buf.write(self.data[self.offset:self.length or None]) self._buf.seek(0) return 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)(data=self.data, offset=self.offset) ret.decode() return ret def reset(self, buf=None): self.data = bytearray() self.offset = 0 self.decoded = False def register_clean_cb(self, cb): global clean_cbs if self.parent is not None: return self.parent.register_clean_cb(cb) else: # get the msg_seq -- if applicable seq = self.get('header', {}).get('sequence_number', None) if seq is not None and seq not in clean_cbs: clean_cbs[seq] = [] # attach the callback clean_cbs[seq].append(cb) def unregister_clean_cb(self): global clean_cbs seq = self.get('header', {}).get('sequence_number', None) msf = self.get('header', {}).get('flags', 0) if (seq is not None) and \ (not msf & NLM_F_REQUEST) and \ seq in clean_cbs: for cb in clean_cbs[seq]: try: cb() except: log.error('Cleanup callback fail: %s' % (cb)) log.error(traceback.format_exc()) del clean_cbs[seq] 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 __ne__(self, rvalue): return not self.__eq__(rvalue) 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: if key == 'attrs': for nla in self[key]: lv = self.get_attr(nla[0]) if isinstance(lv, dict): lv = nlmsg().setvalue(lv) rv = rvalue.get_attr(nla[0]) if isinstance(rv, dict): rv = nlmsg().setvalue(rv) # this strange condition means a simple thing: # None, 0, empty container and NotInitialized in # that context should be treated as equal. if (lv != rv) and not \ ((not lv or lv is NotInitialized) and (not rv or rv is NotInitialized)): return False else: lv = self.get(key) rv = rvalue.get(key) if (lv != rv) and not \ ((not lv or lv is NotInitialized) and (not rv or rv is NotInitialized)): return False 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 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 ''' offset = self.offset global cache_hdr global clean_cbs # Decode the header if self.header is not None: ## # ~~ self['header'][name] = struct.unpack_from(...) # # Instead of `struct.unpack()` all the NLA headers, it is # much cheaper to cache decoded values. The resulting dict # will be not much bigger than some hundreds ov values. # # The code might look ugly, but line_profiler shows here # a notable performance gain. # # The chain is: # dict.get(key, None) or dict.set(unpack(key, ...)) or dict[key] # # If there is no such key in the dict, get() returns None, and # Python executes __setitem__(), which always return None, and # then dict[key] is returned. # # If the key exists, the statement after the first `or` is not # executed. if self.is_nla: key = tuple(self.data[offset:offset+4]) self['header'] = cache_hdr.get(key, None) or \ (cache_hdr .__setitem__(key, dict(zip(('length', 'type'), struct.unpack_from('HH', self.data, offset))))) or \ cache_hdr[key] ## offset += 4 self.length = self['header']['length'] else: for name, fmt in self.header: self['header'][name] = struct.unpack_from(fmt, self.data, offset)[0] offset += struct.calcsize(fmt) # update length from header # it can not be less than 4 self.length = max(self['header']['length'], 4) # handle the array case if self._nla_array: self.setvalue([]) while offset < self.offset + self.length: cell = type(self)(data=self.data, offset=offset, parent=self) cell._nla_array = False if cell.cell_header is not None: cell.header = cell.cell_header cell.decode() self.value.append(cell) offset += (cell.length + 4 - 1) & ~ (4 - 1) else: self._ft_decode(self, offset) if clean_cbs: self.unregister_clean_cb() self.decoded = True def encode(self): ''' Encode the message into the binary buffer:: msg.encode() sock.send(msg.data) 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) ''' offset = self.offset diff = 0 # reserve space for the header if self.header is not None: hsize = struct.calcsize(''.join([x[1] for x in self.header])) self.data.extend([0] * hsize) offset += hsize # handle the array case if self._nla_array: for value in self.getvalue(): cell = type(self)(data=self.data, offset=offset, parent=self) cell._nla_array = False if cell.cell_header is not None: cell.header = cell.cell_header cell.setvalue(value) cell.encode() offset += (cell.length + 4 - 1) & ~ (4 - 1) elif self.getvalue() is not None: for name, fmt in self.fields: value = self[name] if fmt == 's': length = len(value) efmt = '%is' % (length) elif fmt == 'z': length = len(value) + 1 efmt = '%is' % (length) else: length = struct.calcsize(fmt) efmt = fmt self.data.extend([0] * 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': struct.pack_into(efmt, self.data, offset) elif type(value) in (list, tuple, set): struct.pack_into(efmt, self.data, offset, *value) else: struct.pack_into(efmt, self.data, offset, value) except struct.error: log.error(''.join(traceback.format_stack())) log.error(traceback.format_exc()) log.error("error pack: %s %s %s" % (efmt, value, type(value))) raise offset += length diff = ((offset + 4 - 1) & ~ (4 - 1)) - offset offset += diff self.data.extend([0] * diff) # write NLA chain if self.nla_map: offset = self.encode_nlas(offset) # calculate the size and write it if self.header is not None: self.length = self['header']['length'] = (offset - self.offset - diff) offset = self.offset for name, fmt in self.header: struct.pack_into(fmt, self.data, offset, self['header'].get(name, 0)) offset += struct.calcsize(fmt) def setvalue(self, value): if isinstance(value, dict): self.update(value) if 'attrs' in value: self['attrs'] = [] for nla in value['attrs']: nlv = nlmsg_base() nlv.setvalue(nla[1]) self['attrs'].append([nla[0], nlv.getvalue()]) else: try: value = self._r_value_map.get(value, value) except TypeError: pass self['value'] = value self.value = value return self def get_encoded(self, attr, default=None): ''' Return the first encoded NLA by name ''' cells = [i[1] for i in self['attrs'] if i[0] == attr] if cells: return cells[0] def get_nested(self, *attrs): ''' Return nested NLA or None ''' pointer = self for attr in attrs: pointer = pointer.get_attr(attr) if pointer is None: return return pointer def get_attr(self, attr, default=None): ''' Return the first NLA with that name or None ''' try: attrs = self.get_attrs(attr) except KeyError: return default if attrs: return attrs[0] else: return default def get_attrs(self, attr): ''' Return attrs by name or an empty list ''' return [i[1] for i in self['attrs'] if i[0] == attr] def __setstate__(self, state): return self.load(state) def __reduce__(self): return (type(self), (), self.dump()) def load(self, dump): ''' Load packet from a dict:: ipr = IPRoute() lo = ipr.link('dump', ifname='lo')[0] msg_type, msg_value = type(lo), lo.dump() ... lo = msg_type() lo.load(msg_value) The same methods -- `dump()`/`load()` -- implement the pickling protocol for the nlmsg class, see `__reduce__()` and `__setstate__()`. ''' if isinstance(dump, dict): for (k, v) in dump.items(): if k == 'header': self['header'].update(dump['header']) else: self[k] = v else: self.setvalue(dump) return self def dump(self): ''' Dump packet as a dict ''' a = self.getvalue() if isinstance(a, dict): ret = {} for (k, v) in a.items(): if k == 'header': ret['header'] = dict(a['header']) elif k == 'attrs': ret['attrs'] = attrs = [] for i in a['attrs']: if isinstance(i[1], nlmsg_base): attrs.append([i[0], i[1].dump()]) elif isinstance(i[1], set): attrs.append([i[0], tuple(i[1])]) else: attrs.append([i[0], i[1]]) else: ret[k] = v else: ret = a return ret 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_map.get(self['value'], self['value']) return self @staticmethod def _ft_decode_zstring(self, offset): self['value'], = struct.unpack_from('%is' % (self.length - 5), self.data, offset) @staticmethod def _ft_decode_string(self, offset): self['value'], = struct.unpack_from('%is' % (self.length - 4), self.data, offset) @staticmethod def _ft_decode_packed(self, offset): names = [] fmt = '' for field in self.fields: names.append(field[0]) fmt += field[1] value = struct.unpack_from(fmt, self.data, offset) values = list(value) for name in names: if name[0] != '_': self[name] = values.pop(0) # read NLA chain if self.nla_map: offset = (offset + 4 - 1) & ~ (4 - 1) try: self.decode_nlas(offset) except Exception as e: log.warning(traceback.format_exc()) raise NetlinkNLADecodeError(e) else: del self['attrs'] if self['value'] is NotInitialized: del self['value'] @staticmethod def _ft_decode_generic(self, offset): global cache_fmt for name, fmt in self.fields: ## # ~~ size = struct.calcsize(efmt) # # The use of the cache gives here a tiny performance # improvement, but it is an improvement anyways # size = cache_fmt.get(fmt, None) or \ cache_fmt.__setitem__(fmt, struct.calcsize(fmt)) or \ cache_fmt[fmt] ## value = struct.unpack_from(fmt, self.data, offset) offset += size if len(value) == 1: self[name] = value[0] else: self[name] = value # read NLA chain if self.nla_map: offset = (offset + 4 - 1) & ~ (4 - 1) try: self.decode_nlas(offset) except Exception as e: log.warning(traceback.format_exc()) raise NetlinkNLADecodeError(e) else: del self['attrs'] if self['value'] is NotInitialized: del self['value'] def compile_ft(self): global cache_jit if self.fields and self.fields[0][1] == 's': self._ft_decode = self._ft_decode_string elif self.fields and self.fields[0][1] == 'z': self._ft_decode = self._ft_decode_zstring elif self.pack == 'struct': self._ft_decode = self._ft_decode_packed else: self._ft_decode = self._ft_decode_generic cache_jit[id(self.__class__)] = {'ft_decode': self._ft_decode} def compile_nla(self): # clean up NLA mappings t_nla_map = {} r_nla_map = {} # fix nla flags nla_map = [] for item in self.nla_map: if not isinstance(item[-1], int): item = list(item) item.append(0) nla_map.append(item) # detect, whether we have pre-defined keys if not isinstance(nla_map[0][0], int): # create enumeration nla_types = enumerate((i[0] for i in 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], k[0][2]) for k in zip(nla_map, nla_types)] else: zipped = nla_map for (key, name, nla_class, nla_flags) in zipped: # it is an array if nla_class[0] == '*': nla_class = nla_class[1:] nla_array = True else: nla_array = False # are there any init call in the string? lb = nla_class.find('(') rb = nla_class.find(')') if 0 < lb < rb: init = nla_class[lb + 1:rb] nla_class = nla_class[:lb] else: init = None # lookup NLA class if nla_class == 'recursive': nla_class = type(self) else: nla_class = getattr(self, nla_class) # update mappings prime = {'class': nla_class, 'type': key, 'name': name, 'nla_flags': nla_flags, 'nla_array': nla_array, 'init': init} t_nla_map[key] = r_nla_map[name] = prime self.__class__.__t_nla_map = t_nla_map self.__class__.__r_nla_map = r_nla_map self.__class__.__compiled_nla = True def encode_nlas(self, offset): ''' Encode the NLA chain. Should not be called manually, since it is called from `encode()` routine. ''' r_nla_map = self.__class__.__r_nla_map for i in range(len(self['attrs'])): cell = self['attrs'][i] if cell[0] in r_nla_map: prime = r_nla_map[cell[0]] msg_class = prime['class'] # is it a class or a function? if isinstance(msg_class, types.FunctionType): # if it is a function -- use it to get the class msg_class = msg_class(self) # encode NLA nla = msg_class(data=self.data, offset=offset, parent=self, init=prime['init']) nla._nla_flags |= prime['nla_flags'] if isinstance(cell, tuple) and len(cell) > 2: nla._nla_flags |= cell[2] nla._nla_array = prime['nla_array'] nla['header']['type'] = prime['type'] | nla._nla_flags nla.setvalue(cell[1]) try: nla.encode() except: raise else: nla.decoded = True self['attrs'][i] = nla_slot(prime['name'], nla) offset += (nla.length + 4 - 1) & ~ (4 - 1) return offset def decode_nlas(self, offset): ''' Decode the NLA chain. Should not be called manually, since it is called from `decode()` routine. ''' t_nla_map = self.__class__.__t_nla_map while offset - self.offset <= self.length - 4: nla = None # pick the length and the type (length, base_msg_type) = struct.unpack_from('HH', self.data, offset) # first two bits of msg_type are flags: msg_type = base_msg_type & ~(NLA_F_NESTED | NLA_F_NET_BYTEORDER) # rewind to the beginning length = min(max(length, 4), (self.length - offset + self.offset)) # we have a mapping for this NLA if msg_type in t_nla_map: prime = t_nla_map[msg_type] # get the class msg_class = t_nla_map[msg_type]['class'] # is it a class or a function? if isinstance(msg_class, types.FunctionType): # if it is a function -- use it to get the class msg_class = msg_class(self, data=self.data, offset=offset) # decode NLA nla = msg_class(data=self.data, offset=offset, parent=self, length=length, init=prime['init']) nla._nla_array = prime['nla_array'] nla._nla_flags = base_msg_type & (NLA_F_NESTED | NLA_F_NET_BYTEORDER) name = prime['name'] else: name = 'UNKNOWN' nla = nla_base(data=self.data, offset=offset, length=length) self['attrs'].append(nla_slot(name, nla)) offset += (length + 4 - 1) & ~ (4 - 1) class nla_slot(object): __slots__ = ( "cell", ) def __init__(self, name, value): self.cell = (name, value) def try_to_decode(self): try: cell = self.cell[1] if not cell.decoded: cell.decode() return True except Exception: log.warning("decoding %s" % (self.cell[0])) log.warning(traceback.format_exc()) return False def get_value(self): cell = self.cell[1] if self.try_to_decode(): return cell.getvalue() else: return cell.data[cell.offset:cell.offset+cell.length] def get_flags(self): if self.try_to_decode(): return self.cell[1]._nla_flags return None def __getitem__(self, key): if key == 1: return self.get_value() elif key == 0: return self.cell[0] elif isinstance(key, slice): s = list(self.cell.__getitem__(key)) if self.cell[1] in s: s[s.index(self.cell[1])] = self.get_value() return s else: raise IndexError(key) def __repr__(self): if self.get_flags(): return repr((self.cell[0], self.get_value(), self.get_flags())) return repr((self.cell[0], self.get_value())) class nla_base(nlmsg_base): ''' The NLA base class. Use `nla_header` class as the header. ''' __slots__ = () is_nla = True header = (('length', 'H'), ('type', 'H')) class nlmsg_atoms(nlmsg_base): ''' A collection of base NLA types ''' __slots__ = () class none(nla_base): ''' 'none' type is used to skip decoding of NLA. You can also use 'hex' type to dump NLA's content. ''' __slots__ = () def decode(self): nla_base.decode(self) self.value = None class flag(nla_base): ''' 'flag' type is used to denote attrs that have no payload ''' __slots__ = () fields = [] def decode(self): nla_base.decode(self) self.value = True class uint8(nla_base): __slots__ = () fields = [('value', 'B')] class uint16(nla_base): __slots__ = () fields = [('value', 'H')] class uint32(nla_base): __slots__ = () fields = [('value', 'I')] class uint64(nla_base): __slots__ = () fields = [('value', 'Q')] class int32(nla_base): __slots__ = () fields = [('value', 'i')] class be8(nla_base): __slots__ = () fields = [('value', '>B')] class be16(nla_base): __slots__ = () fields = [('value', '>H')] class be32(nla_base): __slots__ = () fields = [('value', '>I')] class be64(nla_base): __slots__ = () fields = [('value', '>Q')] class ipXaddr(nla_base): __slots__ = () fields = [('value', 's')] family = None def encode(self): self['value'] = inet_pton(self.family, self.value) nla_base.encode(self) def decode(self): nla_base.decode(self) self.value = inet_ntop(self.family, self['value']) class ip4addr(ipXaddr): ''' Explicit IPv4 address type class. ''' __slots__ = () family = AF_INET class ip6addr(ipXaddr): ''' Explicit IPv6 address type class. ''' __slots__ = () family = 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. ''' __slots__ = () fields = [('value', 's')] def encode(self): # use real provided family, not implicit if self.value.find(':') > -1: family = AF_INET6 else: family = AF_INET self['value'] = inet_pton(family, self.value) nla_base.encode(self) def decode(self): nla_base.decode(self) # use real provided family, not implicit if self.length > 8: family = AF_INET6 else: family = AF_INET self.value = inet_ntop(family, self['value']) class target(nla_base): ''' A universal target class. The target type depends on the msg family: * AF_INET: IPv4 addr, string: "127.0.0.1" * AF_INET6: IPv6 addr, string: "::1" * AF_MPLS: MPLS labels, 0 .. k: [{"label": 0x20, "ttl": 16}, ...] ''' __slots__ = () fields = [('value', 's')] family = None own_parent = True def get_family(self): if self.family is not None: return self.family pointer = self while pointer.parent is not None: pointer = pointer.parent return pointer.get('family', AF_UNSPEC) def encode(self): family = self.get_family() if family in (AF_INET, AF_INET6): self['value'] = inet_pton(family, self.value) elif family == AF_MPLS: self['value'] = b'' if isinstance(self.value, (set, list, tuple)): labels = self.value else: if 'label' in self: labels = [{'label': self.get('label', 0), 'tc': self.get('tc', 0), 'bos': self.get('bos', 0), 'ttl': self.get('ttl', 0)}] else: labels = [] for record in labels: label = (record.get('label', 0) << 12) |\ (record.get('tc', 0) << 9) |\ ((1 if record.get('bos') else 0) << 8) |\ record.get('ttl', 0) self['value'] += struct.pack('>I', label) else: raise TypeError('socket family not supported') nla_base.encode(self) def decode(self): nla_base.decode(self) family = self.get_family() if family in (AF_INET, AF_INET6): self.value = inet_ntop(family, self['value']) elif family == AF_MPLS: self.value = [] for i in range(len(self['value']) // 4): label = struct.unpack('>I', self['value'][i*4:i*4+4])[0] record = {'label': (label & 0xFFFFF000) >> 12, 'tc': (label & 0x00000E00) >> 9, 'bos': (label & 0x00000100) >> 8, 'ttl': label & 0x000000FF} self.value.append(record) else: raise TypeError('socket family not supported') class mpls_target(target): __slots__ = () family = AF_MPLS class l2addr(nla_base): ''' Decode MAC address. ''' __slots__ = () 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. ''' __slots__ = () fields = [('value', 's')] def decode(self): nla_base.decode(self) self.value = hexdump(self['value']) class array(nla_base): ''' Array of simple data type ''' __slots__ = ( "_fmt", ) fields = [('value', 's')] own_parent = True @property def fmt(self): # try to get format from parent # work only with elementary types if getattr(self, "_fmt", None) is not None: return self._fmt try: fclass = getattr(self.parent, self._nla_init) self._fmt = fclass.fields[0][1] except Exception: self._fmt = self._nla_init return self._fmt def encode(self): fmt = '%s%i%s' % (self.fmt[:-1], len(self.value), self.fmt[-1:]) self['value'] = struct.pack(fmt, self.value) nla_base.encode(self) def decode(self): nla_base.decode(self) data_length = len(self['value']) element_size = struct.calcsize(self.fmt) array_size = data_length // element_size trail = (data_length % element_size) or -data_length data = self['value'][:-trail] fmt = '%s%i%s' % (self.fmt[:-1], array_size, self.fmt[-1:]) self.value = struct.unpack(fmt, data) class cdata(nla_base): ''' Binary data ''' __slots__ = () fields = [('value', 's')] class string(nla_base): ''' UTF-8 string. ''' __slots__ = () fields = [('value', 's')] 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) self.value = self['value'] if sys.version_info[0] >= 3: try: self.value = self.value.decode('utf-8') except UnicodeDecodeError: pass # Failed to decode, keep undecoded value class asciiz(string): ''' Zero-terminated string. ''' __slots__ = () # FIXME: move z-string hacks from general decode here? fields = [('value', 'z')] # FIXME: support NLA_FLAG and NLA_MSECS as well. # # aliases to support standard kernel attributes: # binary = cdata # NLA_BINARY nul_string = asciiz # NLA_NUL_STRING class nla(nla_base, nlmsg_atoms): ''' Main NLA class ''' __slots__ = () def decode(self): nla_base.decode(self) del self['header'] class nlmsg(nlmsg_atoms): ''' Main netlink message class ''' __slots__ = () header = (('length', 'I'), ('type', 'H'), ('flags', 'H'), ('sequence_number', 'I'), ('pid', 'I')) class genlmsg(nlmsg): ''' Generic netlink message ''' __slots__ = () fields = (('cmd', 'B'), ('version', 'B'), ('reserved', 'H')) class ctrlmsg(genlmsg): ''' Netlink control message ''' __slots__ = () # FIXME: to be extended nla_map = (('CTRL_ATTR_UNSPEC', 'none'), ('CTRL_ATTR_FAMILY_ID', 'uint16'), ('CTRL_ATTR_FAMILY_NAME', 'asciiz'), ('CTRL_ATTR_VERSION', 'uint32'), ('CTRL_ATTR_HDRSIZE', 'uint32'), ('CTRL_ATTR_MAXATTR', 'uint32'), ('CTRL_ATTR_OPS', '*ops'), ('CTRL_ATTR_MCAST_GROUPS', '*mcast_groups')) class ops(nla): __slots__ = () nla_map = (('CTRL_ATTR_OP_UNSPEC', 'none'), ('CTRL_ATTR_OP_ID', 'uint32'), ('CTRL_ATTR_OP_FLAGS', 'uint32')) class mcast_groups(nla): __slots__ = () nla_map = (('CTRL_ATTR_MCAST_GRP_UNSPEC', 'none'), ('CTRL_ATTR_MCAST_GRP_NAME', 'asciiz'), ('CTRL_ATTR_MCAST_GRP_ID', 'uint32')) pyroute2-0.4.21/pyroute2/netlink/exceptions.py0000664000175000017500000000173613127143031021277 0ustar peetpeet00000000000000import os 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 class IPSetError(NetlinkError): ''' Netlink error with IPSet special error codes. Messages are imported from errcode.c ''' pass pyroute2-0.4.21/pyroute2/netlink/diag/0000775000175000017500000000000013152333366017453 5ustar peetpeet00000000000000pyroute2-0.4.21/pyroute2/netlink/diag/__init__.py0000664000175000017500000000557113127143031021562 0ustar peetpeet00000000000000from socket import AF_UNIX from pyroute2.netlink import nlmsg from pyroute2.netlink import NLM_F_REQUEST from pyroute2.netlink import NLM_F_ROOT from pyroute2.netlink import NLM_F_MATCH from pyroute2.netlink import NETLINK_SOCK_DIAG from pyroute2.netlink.nlsocket import Marshal from pyroute2.netlink.nlsocket import NetlinkSocket SOCK_DIAG_BY_FAMILY = 20 SOCK_DESTROY = 21 # states SS_UNKNOWN = 0 SS_ESTABLISHED = 1 SS_SYN_SENT = 2 SS_SYN_RECV = 3 SS_FIN_WAIT1 = 4 SS_FIN_WAIT2 = 5 SS_TIME_WAIT = 6 SS_CLOSE = 7 SS_CLOSE_WAIT = 8 SS_LAST_ACK = 9 SS_LISTEN = 10 SS_CLOSING = 11 SS_MAX = 12 SS_ALL = ((1 << SS_MAX) - 1) SS_CONN = (SS_ALL & ~((1 << SS_LISTEN) | (1 << SS_CLOSE) | (1 << SS_TIME_WAIT) | (1 << SS_SYN_RECV))) class sock_diag_req(nlmsg): fields = (('sdiag_family', 'B'), ('sdiag_protocol', 'B')) UDIAG_SHOW_NAME = 0x01 UDIAG_SHOW_VFS = 0x02 UDIAG_SHOW_PEER = 0x04 UDIAG_SHOW_ICONS = 0x08 UDIAG_SHOW_RQLEN = 0x10 UDIAG_SHOW_MEMINFO = 0x20 class unix_diag_req(nlmsg): fields = (('sdiag_family', 'B'), ('sdiag_protocol', 'B'), ('__pad', 'H'), ('udiag_states', 'I'), ('udiag_ino', 'I'), ('udiag_show', 'I'), ('udiag_cookie', 'Q')) class unix_diag_msg(nlmsg): fields = (('udiag_family', 'B'), ('udiag_type', 'B'), ('udiag_state', 'B'), ('__pad', 'B'), ('udiag_ino', 'I'), ('udiag_cookie', 'Q')) class MarshalDiag(Marshal): type_format = 'B' # The family goes after the nlmsg header, # IHHII = 4 + 2 + 2 + 4 + 4 = 16 bytes type_offset = 16 # Please notice that the SOCK_DIAG Marshal # uses not the nlmsg type, but sdiag_family # to choose the proper class msg_map = {AF_UNIX: unix_diag_msg} class DiagSocket(NetlinkSocket): ''' Usage:: from pyroute2 import DiagSocket with DiagSocket() as ds: ds.bind() sstats = ds.get_sock_stats() ''' def __init__(self, fileno=None): super(DiagSocket, self).__init__(NETLINK_SOCK_DIAG) self.marshal = MarshalDiag() def get_sock_stats(self, family=AF_UNIX, states=SS_ALL, show=(UDIAG_SHOW_NAME | UDIAG_SHOW_VFS | UDIAG_SHOW_PEER | UDIAG_SHOW_ICONS)): ''' Get sockets statistics. ''' if family == AF_UNIX: req = unix_diag_req() else: raise NotImplementedError() req['sdiag_family'] = family req['udiag_states'] = states req['udiag_show'] = show return self.nlm_request(req, SOCK_DIAG_BY_FAMILY, NLM_F_REQUEST | NLM_F_ROOT | NLM_F_MATCH) pyroute2-0.4.21/pyroute2/netlink/event/0000775000175000017500000000000013152333366017670 5ustar peetpeet00000000000000pyroute2-0.4.21/pyroute2/netlink/event/acpi_event.py0000664000175000017500000000173013127143031022346 0ustar peetpeet00000000000000''' ''' from pyroute2.netlink import nla from pyroute2.netlink import genlmsg from pyroute2.netlink.nlsocket import Marshal from pyroute2.netlink.event import EventSocket ACPI_GENL_CMD_UNSPEC = 0 ACPI_GENL_CMD_EVENT = 1 class acpimsg(genlmsg): nla_map = (('ACPI_GENL_ATTR_UNSPEC', 'none'), ('ACPI_GENL_ATTR_EVENT', 'acpiev')) class acpiev(nla): fields = (('device_class', '20s'), ('bus_id', '15s'), ('type', 'I'), ('data', 'I')) def decode(self): nla.decode(self) dc = self['device_class'] bi = self['bus_id'] self['device_class'] = dc[:dc.find(b'\x00')] self['bus_id'] = bi[:bi.find(b'\x00')] class MarshalAcpiEvent(Marshal): msg_map = {ACPI_GENL_CMD_UNSPEC: acpimsg, ACPI_GENL_CMD_EVENT: acpimsg} class AcpiEventSocket(EventSocket): marshal_class = MarshalAcpiEvent genl_family = 'acpi_event' pyroute2-0.4.21/pyroute2/netlink/event/thermal_event.py0000664000175000017500000000120413127143031023062 0ustar peetpeet00000000000000''' TODO: add THERMAL_GENL_ATTR_EVENT structure ''' from pyroute2.netlink import genlmsg from pyroute2.netlink.nlsocket import Marshal from pyroute2.netlink.event import EventSocket THERMAL_GENL_CMD_UNSPEC = 0 THERMAL_GENL_CMD_EVENT = 1 class thermal_msg(genlmsg): nla_map = (('THERMAL_GENL_ATTR_UNSPEC', 'none'), ('THERMAL_GENL_ATTR_EVENT', 'hex')) # to be done class MarshalThermalEvent(Marshal): msg_map = {THERMAL_GENL_CMD_UNSPEC: thermal_msg, THERMAL_GENL_CMD_EVENT: thermal_msg} class ThermalEventSocket(EventSocket): marshal_class = MarshalThermalEvent genl_family = 'thermal_event' pyroute2-0.4.21/pyroute2/netlink/event/dquot.py0000664000175000017500000000233213127143031021364 0ustar peetpeet00000000000000''' VFS_DQUOT module ================ Usage:: from pyroute2 import DQuotSocket ds = DQuotSocket() msgs = ds.get() Please notice, that `.get()` always returns a list, even if only one message arrived. To get NLA values:: msg = msgs[0] uid = msg.get_attr('QUOTA_NL_A_EXCESS_ID') major = msg.get_attr('QUOTA_NL_A_DEV_MAJOR') minor = msg.get_attr('QUOTA_NL_A_DEV_MINOR') ''' from pyroute2.netlink import genlmsg from pyroute2.netlink.nlsocket import Marshal from pyroute2.netlink.event import EventSocket QUOTA_NL_C_UNSPEC = 0 QUOTA_NL_C_WARNING = 1 class dquotmsg(genlmsg): prefix = 'QUOTA_NL_A_' nla_map = (('QUOTA_NL_A_UNSPEC', 'none'), ('QUOTA_NL_A_QTYPE', 'uint32'), ('QUOTA_NL_A_EXCESS_ID', 'uint64'), ('QUOTA_NL_A_WARNING', 'uint32'), ('QUOTA_NL_A_DEV_MAJOR', 'uint32'), ('QUOTA_NL_A_DEV_MINOR', 'uint32'), ('QUOTA_NL_A_CAUSED_ID', 'uint64'), ('QUOTA_NL_A_PAD', 'uint64')) class MarshalDQuot(Marshal): msg_map = {QUOTA_NL_C_UNSPEC: dquotmsg, QUOTA_NL_C_WARNING: dquotmsg} class DQuotSocket(EventSocket): marshal_class = MarshalDQuot genl_family = 'VFS_DQUOT' pyroute2-0.4.21/pyroute2/netlink/event/__init__.py0000664000175000017500000000134213127143031021767 0ustar peetpeet00000000000000 from pyroute2.config import kernel from pyroute2.netlink.generic import GenericNetlinkSocket class EventSocket(GenericNetlinkSocket): marshal_class = None genl_family = None def __init__(self): GenericNetlinkSocket.__init__(self) self.marshal = self.marshal_class() if kernel[0] <= 2: self.bind(groups=0xffffff) else: self.bind() for group in self.mcast_groups: self.add_membership(group) def bind(self, groups=0, async=False): GenericNetlinkSocket.bind(self, self.genl_family, self.marshal_class.msg_map[0], groups, None, async) pyroute2-0.4.21/pyroute2/remote/0000775000175000017500000000000013152333366016376 5ustar peetpeet00000000000000pyroute2-0.4.21/pyroute2/remote/__init__.py0000664000175000017500000003124013127143031020475 0ustar peetpeet00000000000000import os import atexit import pickle import select import signal import socket import struct import threading import traceback from io import BytesIO from socket import SOL_SOCKET from socket import SO_RCVBUF from pyroute2 import IPRoute from pyroute2.common import uuid32 from pyroute2.netlink.nlsocket import NetlinkMixin from pyroute2.netlink.rtnl.iprsocket import MarshalRtnl from pyroute2.iproute import IPRouteMixin try: from urlparse import urlparse except ImportError: from urllib.parse import urlparse class Transport(object): ''' A simple transport protocols to send objects between two end-points. Requires an open socket-like object at init. ''' def __init__(self, sock): self.sock = sock self.sock.setblocking(True) def fileno(self): return self.sock.fileno() def send(self, obj): dump = BytesIO() pickle.dump(obj, dump) packet = struct.pack("II", len(dump.getvalue()) + 8, 0) packet += dump.getvalue() self.sock.sendall(packet) def recv(self): length, offset = struct.unpack("II", self.sock.recv(8)) dump = BytesIO() actual = 0 while actual < (length - 8): chunk = self.sock.recv(length - 8 - actual) actual += len(chunk) dump.write(chunk) dump.seek(0) ret = pickle.load(dump) return ret def close(self): self.sock.close() class SocketChannel(Transport): ''' A data channel over ordinary AF_INET socket. ''' def __init__(self, host, port): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((host, port)) Transport.__init__(self, sock) class ProxyChannel(object): def __init__(self, channel, stage): self.target = channel self.stage = stage def send(self, data): return self.target.send({'stage': self.stage, 'data': data, 'error': None}) def Server(cmdch, brdch): ''' A server routine to run an IPRoute object and expose it via custom RPC. many TODOs: * document the protocol * provide not only IPRoute RPC Messages sent via channels are dictionaries with predefined structure. There are 4 s.c. stages:: init (server <-----> client) command (server <-----> client) broadcast (server ------> client) shutdown (server <------ client) Stage 'init' is used during initialization. The client establishes connections to the server and announces them by sending a single message via each channel:: {'stage': 'init', 'domain': ch_domain, 'client': client.uuid} Here, the client uuid is used to group all the connections of the same client and `ch_domain` is either 'command', or 'broadcast'. The latter will become a unidirectional channel from the server to the client, all data that arrives on the server side via netlink socket will be forwarded to the broadcast channel. The command channel will be used to make RPC calls and to shut the worker thread down before the client disconnects from the server. When all the registration is done, the server sends a single message via the command channel:: {'stage': 'init', 'error': exception or None} If the `error` field is None, everything is ok. If it is an exception, the init is failed and the exception should be thrown on the client side. In the runtime, all the data that arrives on the netlink socket fd, is to be forwarded directly via the broadcast channel. Commands are handled with the `command` stage:: # request {'stage': 'command', 'name': str, 'cookie': cookie, 'argv': [...], 'kwarg': {...}} # response {'stage': 'command', 'error': exception or None, 'return': retval, 'cookie': cookie} Right now the protocol is synchronous, so there is not need in cookie yet. But in some future it can turn into async, and then cookies will be used to match messages. The final stage is 'shutdown'. It terminates the worker thread, has no response and no messages can passed after. ''' def close(s, frame): # just leave everything else as is brdch.send({'stage': 'signal', 'data': s}) try: ipr = IPRoute() lock = ipr._sproxy.lock ipr._s_channel = ProxyChannel(brdch, 'broadcast') except Exception as e: cmdch.send({'stage': 'init', 'error': e}) return 255 inputs = [ipr.fileno(), cmdch.fileno()] outputs = [] # all is OK so far cmdch.send({'stage': 'init', 'error': None}) signal.signal(signal.SIGHUP, close) signal.signal(signal.SIGINT, close) signal.signal(signal.SIGTERM, close) # 8<------------------------------------------------------------- while True: try: events, _, _ = select.select(inputs, outputs, inputs) except: continue for fd in events: if fd == ipr.fileno(): bufsize = ipr.getsockopt(SOL_SOCKET, SO_RCVBUF) // 2 with lock: error = None data = None try: data = ipr.recv(bufsize) except Exception as e: error = e error.tb = traceback.format_exc() brdch.send({'stage': 'broadcast', 'data': data, 'error': error}) elif fd == cmdch.fileno(): cmd = cmdch.recv() if cmd['stage'] == 'shutdown': ipr.close() return elif cmd['stage'] == 'reconstruct': error = None try: msg = cmd['argv'][0]() msg.load(pickle.loads(cmd['argv'][1])) msg.encode() ipr.sendto_gate(msg, cmd['argv'][2]) except Exception as e: error = e error.tb = traceback.format_exc() cmdch.send({'stage': 'reconstruct', 'error': error, 'return': None, 'cookie': cmd['cookie']}) elif cmd['stage'] == 'command': error = None try: ret = getattr(ipr, cmd['name'])(*cmd['argv'], **cmd['kwarg']) except Exception as e: error = e error.tb = traceback.format_exc() cmdch.send({'stage': 'command', 'error': error, 'return': ret, 'cookie': cmd['cookie']}) class Client(object): brdch = None cmdch = None def __init__(self): self.cmdlock = threading.Lock() self.lock = threading.Lock() self.closed = False init = self.cmdch.recv() if init['stage'] != 'init': raise TypeError('incorrect protocol init') if init['error'] is not None: raise init['error'] else: atexit.register(self.close) self.sendto_gate = self._gate def _gate(self, msg, addr): with self.cmdlock: self.cmdch.send({'stage': 'reconstruct', 'cookie': None, 'name': None, 'argv': [type(msg), pickle.dumps(msg.dump()), addr], 'kwarg': None}) ret = self.cmdch.recv() if ret['error'] is not None: raise ret['error'] return ret['return'] def recv(self, bufsize, flags=0): msg = None while True: msg = self.brdch.recv() if msg['stage'] == 'signal': os.kill(os.getpid(), msg['data']) else: break if msg['error'] is not None: raise msg['error'] return msg['data'] def close(self): with self.lock: if not self.closed: self.closed = True self.cmdch.send({'stage': 'shutdown'}) if hasattr(self.cmdch, 'close'): self.cmdch.close() if hasattr(self.brdch, 'close'): self.brdch.close() def proxy(self, cmd, *argv, **kwarg): with self.cmdlock: self.cmdch.send({'stage': 'command', 'cookie': None, 'name': cmd, 'argv': argv, 'kwarg': kwarg}) ret = self.cmdch.recv() if ret['error'] is not None: raise ret['error'] return ret['return'] def fileno(self): return self.brdch.fileno() def bind(self, *argv, **kwarg): if 'async' in kwarg: # do not work with async servers 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 RemoteSocket(NetlinkMixin, Client): def bind(self, *argv, **kwarg): return Client.bind(self, *argv, **kwarg) def close(self): NetlinkMixin.close(self) Client.close(self) def _sendto(self, *argv, **kwarg): return Client.sendto(self, *argv, **kwarg) def _recv(self, *argv, **kwarg): return Client.recv(self, *argv, **kwarg) class Remote(IPRouteMixin, RemoteSocket): ''' Experimental TCP server. Only for debug purposes now. ''' def __init__(self, url): if url.startswith('tcp://'): hostname = urlparse(url).netloc self.cmdch = SocketChannel(hostname, 4336) self.brdch = SocketChannel(hostname, 4336) self.uuid = uuid32() self.cmdch.send({'stage': 'init', 'domain': 'command', 'client': self.uuid}) self.brdch.send({'stage': 'init', 'domain': 'broadcast', 'client': self.uuid}) else: raise TypeError('remote connection type not supported') super(RemoteSocket, self).__init__() self.marshal = MarshalRtnl() def post_init(self): pass class Master(object): def __init__(self, host='localhost', port=4336): self.master_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.master_sock.bind((host, port)) self.new = {} self.clients = {} self.threads = [] def start(self): self.master_sock.listen(4) poll = select.poll() poll.register(self.master_sock, select.POLLIN | select.POLLPRI) while True: for (fd, event) in poll.poll(): if fd == self.master_sock.fileno(): (sock, info) = self.master_sock.accept() self.new[sock.fileno()] = Transport(sock) poll.register(sock, select.POLLIN | select.POLLPRI) elif fd in self.new: init = self.new[fd].recv() if init['client'] in self.clients: client = self.clients.pop(init['client']) client[init['domain']] = self.new.pop(fd) args = (client['command'], client['broadcast']) t = threading.Thread(target=Server, args=args) t.start() self.threads.append(t) poll.unregister(client['command'].sock) poll.unregister(client['broadcast'].sock) else: cid = init['client'] self.clients[cid] = {} self.clients[cid][init['domain']] = self.new.pop(fd) else: raise Exception('lost socket fd') pyroute2-0.4.21/pyroute2/protocols/0000775000175000017500000000000013152333366017127 5ustar peetpeet00000000000000pyroute2-0.4.21/pyroute2/protocols/rawsocket.py0000664000175000017500000000400213123443516021474 0ustar peetpeet00000000000000import struct from ctypes import Structure from ctypes import addressof from ctypes import string_at from ctypes import sizeof from ctypes import c_ushort from ctypes import c_ubyte from ctypes import c_uint from ctypes import c_void_p from socket import socket from socket import htons from socket import AF_PACKET from socket import SOCK_RAW from socket import SOL_SOCKET from pyroute2 import IPRoute ETH_P_ALL = 3 SO_ATTACH_FILTER = 26 class sock_filter(Structure): _fields_ = [('code', c_ushort), # u16 ('jt', c_ubyte), # u8 ('jf', c_ubyte), # u8 ('k', c_uint)] # u32 class sock_fprog(Structure): _fields_ = [('len', c_ushort), ('filter', c_void_p)] def compile_bpf(code): ProgramType = sock_filter * len(code) program = ProgramType(*[sock_filter(*line) for line in code]) sfp = sock_fprog(len(code), addressof(program[0])) return string_at(addressof(sfp), sizeof(sfp)), program class RawSocket(socket): fprog = None def __init__(self, ifname, bpf=None): self.ifname = ifname # lookup the interface details with IPRoute() as ip: for link in ip.get_links(): if link.get_attr('IFLA_IFNAME') == ifname: break else: raise IOError(2, 'Link not found') self.l2addr = link.get_attr('IFLA_ADDRESS') self.ifindex = link['index'] # bring up the socket socket.__init__(self, AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)) socket.bind(self, (self.ifname, ETH_P_ALL)) if bpf: fstring, self.fprog = compile_bpf(bpf) socket.setsockopt(self, SOL_SOCKET, SO_ATTACH_FILTER, fstring) def csum(self, data): if len(data) % 2: data += b'\x00' csum = sum([struct.unpack('>H', data[x*2:x*2+2])[0] for x in range(len(data)//2)]) csum = (csum >> 16) + (csum & 0xffff) csum += csum >> 16 return ~csum & 0xffff pyroute2-0.4.21/pyroute2/protocols/icmp.py0000664000175000017500000000062013123443516020424 0ustar peetpeet00000000000000from pyroute2.protocols import msg class icmpmsg(msg): fields = [('type', 'uint8'), ('code', 'uint8'), ('csum', 'be32')] class icmp_router_adv(icmpmsg): fields = icmpmsg.fields + [('addrs_num', 'uint8'), ('alen', 'uint8'), ('lifetime', 'be32'), ('addrs', 'routers')] pyroute2-0.4.21/pyroute2/protocols/__init__.py0000664000175000017500000002117713123443516021245 0ustar peetpeet00000000000000import struct from socket import inet_ntop from socket import inet_pton from socket import AF_INET from pyroute2.common import basestring # # 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 class msg(dict): buf = None data_len = None fields = () _fields_names = () types = {'uint8': 'B', 'uint16': 'H', 'uint32': 'I', 'be16': '>H', 'ip4addr': {'format': '4s', 'decode': lambda x: inet_ntop(AF_INET, x), 'encode': lambda x: [inet_pton(AF_INET, x)]}, 'l2addr': {'format': '6B', 'decode': lambda x: ':'.join(['%x' % i for i in x]), 'encode': lambda x: [int(i, 16) for i in x.split(':')]}, 'l2paddr': {'format': '6B10s', 'decode': lambda x: ':'.join(['%x' % i for i in x[:6]]), 'encode': lambda x: [int(i, 16) for i in x.split(':')] + [10 * b'\x00']}} def __init__(self, content=None, buf=b'', offset=0, value=None): content = content or {} dict.__init__(self, content) self.buf = buf self.offset = offset self.value = value self._register_fields() def _register_fields(self): self._fields_names = tuple([x[0] for x in self.fields]) def _get_routine(self, mode, fmt): fmt = self.types.get(fmt, fmt) if isinstance(fmt, dict): return (fmt['format'], fmt.get(mode, lambda x: x)) else: return (fmt, lambda x: x) def reset(self): self.buf = b'' def decode(self): self._register_fields() for field in self.fields: name, sfmt = field[:2] fmt, routine = self._get_routine('decode', sfmt) size = struct.calcsize(fmt) value = struct.unpack(fmt, self.buf[self.offset: self.offset + size]) if len(value) == 1: value = value[0] if isinstance(value, basestring) and sfmt[-1] == 's': value = value[:value.find(b'\x00')] self[name] = routine(value) self.offset += size return self def encode(self): self._register_fields() for field in self.fields: name, fmt = field[:2] default = b'\x00' if len(field) <= 2 else field[2] fmt, routine = self._get_routine('encode', fmt) # special case: string if fmt == 'string': self.buf += routine(self[name])[0] else: size = struct.calcsize(fmt) if self[name] is None: if not isinstance(default, basestring): self.buf += struct.pack(fmt, default) else: self.buf += default * (size // len(default)) else: value = routine(self[name]) if not isinstance(value, (set, tuple, list)): value = [value] self.buf += struct.pack(fmt, *value) return self def __getitem__(self, key): try: return dict.__getitem__(self, key) except KeyError: if key in self._fields_names: return None raise class ethmsg(msg): fields = (('dst', 'l2addr'), ('src', 'l2addr'), ('type', 'be16')) class ip6msg(msg): fields = (('version', 'uint8', 6 << 4), ('_flow0', 'uint8'), ('_flow1', 'uint8'), ('_flow2', 'uint8'), ('plen', 'uin16'), ('next_header', 'uint8'), ('hop_limit', 'uint8'), ('src', 'ip6addr'), ('dst', 'ip6addr')) class ip4msg(msg): fields = (('verlen', 'uint8', 0x45), ('dsf', 'uint8'), ('len', 'be16'), ('id', 'be16'), ('flags', 'uint16'), ('ttl', 'uint8', 128), ('proto', 'uint8'), ('csum', 'be16'), ('src', 'ip4addr'), ('dst', 'ip4addr')) class udp4_pseudo_header(msg): fields = (('src', 'ip4addr'), ('dst', 'ip4addr'), ('pad', 'uint8'), ('proto', 'uint8', 17), ('len', 'be16')) class udpmsg(msg): fields = (('sport', 'be16'), ('dport', 'be16'), ('len', 'be16'), ('csum', 'be16')) pyroute2-0.4.21/pyroute2/nftables.py0000664000175000017500000000015013123443516017244 0ustar peetpeet00000000000000''' ''' from pyroute2.netlink.nfnetlink.nftables import NFTSocket class NFTables(NFTSocket): pass pyroute2-0.4.21/pyroute2/netns/0000775000175000017500000000000013152333366016232 5ustar peetpeet00000000000000pyroute2-0.4.21/pyroute2/netns/nslink.py0000664000175000017500000001631013152305337020100 0ustar peetpeet00000000000000''' NetNS ===== A NetNS object is IPRoute-like. It runs in the main network namespace, but also creates a proxy process running in the required netns. All the netlink requests are done via that proxy process. 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. 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') To remove a network namespace:: from pyroute2 import NetNS netns = NetNS('test') netns.close() netns.remove() One should stop it first with `close()`, and only after that run `remove()`. ''' import os import errno import fcntl import atexit import signal import sys import logging from pyroute2 import config from pyroute2.netlink.rtnl.iprsocket import MarshalRtnl from pyroute2.iproute import IPRouteMixin from pyroute2.netns import setns from pyroute2.netns import remove from pyroute2.remote import Server from pyroute2.remote import RemoteSocket log = logging.getLogger(__name__) def NetNServer(netns, cmdch, brdch, 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. ''' signal.signal(signal.SIGINT, signal.SIG_IGN) try: nsfd = setns(netns, flags) except OSError as e: cmdch.send({'stage': 'init', 'error': e}) return e.errno except Exception as e: cmdch.send({'stage': 'init', 'error': OSError(errno.ECOMM, str(e), netns)}) return 255 Server(cmdch, brdch) os.close(nsfd) class NetNS(IPRouteMixin, RemoteSocket): ''' 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 self.cmdch, self._cmdch = config.MpPipe() self.brdch, self._brdch = config.MpPipe() atexit.register(self.close) self.server = config.MpProcess(target=NetNServer, args=(self.netns, self._cmdch, self._brdch, self.flags)) self.server.start() super(NetNS, self).__init__() self.marshal = MarshalRtnl() def clone(self): return type(self)(self.netns, self.flags) def close(self): try: super(NetNS, self).close() except: # something went wrong, force server shutdown self.cmdch.send({'stage': 'shutdown'}) log.error('forced shutdown procedure, clean up netns manually') # force cleanup command channels self.cmdch.close() self.brdch.close() self._cmdch.close() self._brdch.close() # join the server self.server.join() # Workaround for http://bugs.python.org/issue27151 if sys.version_info > (3, 2): try: fcntl.fcntl(self.server.sentinel, fcntl.F_GETFD) except: pass else: os.close(self.server.sentinel) def post_init(self): pass def remove(self): ''' Try to remove this network namespace from the system. ''' remove(self.netns) pyroute2-0.4.21/pyroute2/netns/__init__.py0000664000175000017500000001546413152305337020352 0ustar peetpeet00000000000000''' Netns management overview ========================= Pyroute2 provides basic namespaces management support. Here's a quick overview of typical netns tasks and related pyroute2 tools. Move an interface to a namespace -------------------------------- Though this task is managed not via `netns` module, it should be mentioned here as well. To move an interface to a netns, one should provide IFLA_NET_NS_FD nla in a set link RTNL request. The nla is an open FD number, that refers to already created netns. The pyroute2 library provides also a possibility to specify not a FD number, but a netns name as a string. In that case the library will try to lookup the corresponding netns in the standard location. Create veth and move the peer to a netns with IPRoute:: from pyroute2 import IPRoute ipr = IPRoute() ipr.link('add', ifname='v0p0', kind='veth', peer='v0p1') idx = ipr.link_lookup(ifname='v0p1')[0] ipr.link('set', index=idx, net_ns_fd='netns_name') Create veth and move the peer to a netns with IPDB:: from pyroute2 import IPDB ipdb = IPDB() ipdb.create(ifname='v0p0', kind='veth', peer='v0p1').commit() with ipdb.interfaces.v0p1 as i: i.net_ns_fd = 'netns_name' Manage interfaces within a netns -------------------------------- This task can be done with `NetNS` objects. A `NetNS` object spawns a child and runs it within a netns, providing the same API as `IPRoute` does:: from pyroute2 import NetNS ns = NetNS('netns_name') # do some stuff within the netns ns.close() One can even start `IPDB` on the top of `NetNS`:: from pyroute2 import NetNS from pyroute2 import IPDB ipdb = IPDB(nl=NetNS('netns_name')) # do some stuff within the netns ipdb.release() Spawn a process within a netns ------------------------------ For that purpose one can use `NSPopen` API. It works just as normal `Popen`, but starts a process within a netns. List, set, create and remove netns ---------------------------------- These functions are described below. To use them, import `netns` module:: from pyroute2 import netns netns.listnetns() 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. ''' import io import os import os.path import errno import ctypes import pickle import struct import traceback from pyroute2 import config from pyroute2.common import basestring try: file = file except NameError: file = io.IOBase # FIXME: arch reference __NR = {'x86_': {'64bit': 308}, 'i386': {'32bit': 346}, 'i686': {'32bit': 346}, 'mips': {'32bit': 4344, '64bit': 5303}, # FIXME: NABI32? 'armv': {'32bit': 375}, 'aarc': {'64bit': 268}, # FIXME: EABI vs. OABI? 'ppc6': {'64bit': 350}} __NR_setns = __NR.get(config.machine[:4], {}).get(config.arch, 308) CLONE_NEWNET = 0x40000000 MNT_DETACH = 0x00000002 MS_BIND = 4096 MS_REC = 16384 MS_SHARED = 1 << 20 NETNS_RUN_DIR = '/var/run/netns' def _get_netnspath(name): netnspath = name dirname = os.path.dirname(name) if not dirname: netnspath = '%s/%s' % (NETNS_RUN_DIR, name) netnspath = netnspath.encode('ascii') return netnspath def listnetns(nspath=None): ''' List available network namespaces. ''' if nspath: nsdir = nspath else: nsdir = NETNS_RUN_DIR try: return os.listdir(nsdir) except OSError as e: if e.errno == errno.ENOENT: return [] else: raise def _create(netns, libc=None): libc = libc or ctypes.CDLL('libc.so.6', use_errno=True) netnspath = _get_netnspath(netns) netnsdir = os.path.dirname(netnspath) # 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(ctypes.get_errno(), 'share rundir failed', netns) if libc.mount(netnsdir, netnsdir, b'none', MS_BIND, None) != 0: raise OSError(ctypes.get_errno(), '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(ctypes.get_errno(), 'unshare failed', netns) # bind the namespace if libc.mount(b'/proc/self/ns/net', netnspath, b'none', MS_BIND, None) < 0: raise OSError(ctypes.get_errno(), 'mount failed', netns) def create(netns, libc=None): ''' Create a network namespace. ''' rctl, wctl = os.pipe() pid = os.fork() if pid == 0: # child error = None try: _create(netns, libc) except Exception as e: error = e error.tb = traceback.format_exc() msg = pickle.dumps(error) os.write(wctl, struct.pack('I', len(msg))) os.write(wctl, msg) os._exit(0) else: # parent msglen = struct.unpack('I', os.read(rctl, 4))[0] error = pickle.loads(os.read(rctl, msglen)) os.close(rctl) os.close(wctl) os.waitpid(pid, 0) if error is not None: raise error def remove(netns, libc=None): ''' Remove a network namespace. ''' libc = libc or ctypes.CDLL('libc.so.6', use_errno=True) netnspath = _get_netnspath(netns) libc.umount2(netnspath, MNT_DETACH) os.unlink(netnspath) def setns(netns, flags=os.O_CREAT, libc=None): ''' Set netns for the current process. The flags semantics is the same as for the `open(2)` call: - O_CREAT -- create netns, if doesn't exist - O_CREAT | O_EXCL -- create only if doesn't exist ''' libc = libc or ctypes.CDLL('libc.so.6', use_errno=True) if isinstance(netns, basestring): netnspath = _get_netnspath(netns) if os.path.basename(netns) in listnetns(os.path.dirname(netns)): 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 = nsfd elif isinstance(netns, file): nsfd = netns.fileno() ret = netns elif isinstance(netns, int): nsfd = netns ret = netns else: raise RuntimeError('netns should be a string or an open fd') error = libc.syscall(__NR_setns, nsfd, CLONE_NEWNET) if error != 0: raise OSError(ctypes.get_errno(), 'failed to open netns', netns) return ret pyroute2-0.4.21/pyroute2/netns/process/0000775000175000017500000000000013152333366017710 5ustar peetpeet00000000000000pyroute2-0.4.21/pyroute2/netns/process/__init__.py0000664000175000017500000000325013123443516022016 0ustar peetpeet00000000000000import fcntl import types import subprocess from pyroute2.common import file def _map_api(api, obj): for attr_name in dir(obj): attr = getattr(obj, attr_name) api[attr_name] = {'api': None} api[attr_name]['callable'] = hasattr(attr, '__call__') api[attr_name]['doc'] = attr.__doc__ \ if hasattr(attr, '__doc__') else None class MetaPopen(type): ''' API definition for NSPopen. All this stuff is required to make `help()` function happy. ''' def __init__(cls, *argv, **kwarg): super(MetaPopen, cls).__init__(*argv, **kwarg) # copy docstrings and create proxy slots cls.api = {} _map_api(cls.api, subprocess.Popen) for fname in ('stdin', 'stdout', 'stderr'): m = {} cls.api[fname] = {'callable': False, 'api': m} _map_api(m, file) for ename in ('fcntl', 'ioctl', 'flock', 'lockf'): m[ename] = {'api': None, 'callable': True, 'doc': getattr(fcntl, ename).__doc__} def __dir__(cls): return list(cls.api.keys()) + ['release'] def __getattribute__(cls, key): try: return type.__getattribute__(cls, key) except AttributeError: attr = getattr(subprocess.Popen, key) if isinstance(attr, (types.MethodType, types.FunctionType)): def proxy(*argv, **kwarg): return attr(*argv, **kwarg) proxy.__doc__ = attr.__doc__ proxy.__objclass__ = cls return proxy else: return attr pyroute2-0.4.21/pyroute2/netns/process/proxy.py0000664000175000017500000002253713152305337021451 0ustar peetpeet00000000000000''' NSPopen ======= The `NSPopen` class has nothing to do with netlink at all, but it is required to have a reasonable network namespace support. ''' import sys import fcntl import types import atexit import threading import subprocess from pyroute2 import config from pyroute2.netns import setns from pyroute2.common import metaclass from pyroute2.netns.process import MetaPopen def _handle(result): if result['code'] == 500: raise result['data'] elif result['code'] == 200: return result['data'] else: raise TypeError('unsupported return code') def _make_fcntl(prime, target): def func(*argv, **kwarg): return target(prime.fileno(), *argv, **kwarg) return func def _make_func(target): def func(*argv, **kwarg): return target(*argv, **kwarg) return func def _make_property(name): def func(self): return getattr(self.prime, name) return property(func) class NSPopenFile(object): def __init__(self, prime): self.prime = prime for aname in dir(prime): if aname.startswith('_'): continue target = getattr(prime, aname) if isinstance(target, (types.BuiltinMethodType, types.MethodType)): func = _make_func(target) func.__name__ = aname func.__doc__ = getattr(target, '__doc__', '') setattr(self, aname, func) del func else: setattr(self.__class__, aname, _make_property(aname)) for fname in ('fcntl', 'ioctl', 'flock', 'lockf'): target = getattr(fcntl, fname) func = _make_fcntl(prime, target) func.__name__ = fname func.__doc__ = getattr(target, '__doc__', '') setattr(self, fname, func) del func def NSPopenServer(nsname, flags, channel_in, channel_out, argv, kwarg): # set netns try: setns(nsname, flags=flags) except Exception as e: channel_out.put(e) return # create the Popen object child = subprocess.Popen(*argv, **kwarg) for fname in ['stdout', 'stderr', 'stdin']: obj = getattr(child, fname) if obj is not None: fproxy = NSPopenFile(obj) setattr(child, fname, fproxy) # send the API map channel_out.put(None) while True: # synchronous mode # 1. get the command from the API try: call = channel_in.get() except: (et, ev, tb) = sys.exc_info() try: channel_out.put({'code': 500, 'data': ev}) except: pass break # 2. stop? if call['name'] == 'release': break # 3. run the call try: # get the object namespace ns = call.get('namespace') obj = child if ns: for step in ns.split('.'): obj = getattr(obj, step) attr = getattr(obj, call['name']) if isinstance(attr, (types.MethodType, types.FunctionType, types.BuiltinMethodType)): result = attr(*call['argv'], **call['kwarg']) else: result = attr channel_out.put({'code': 200, 'data': result}) except: (et, ev, tb) = sys.exc_info() channel_out.put({'code': 500, 'data': ev}) child.wait() class ObjNS(object): ns = None def __enter__(self): pass def __exit__(self, exc_type, exc_value, traceback): pass def __getattribute__(self, key): try: return object.__getattribute__(self, key) except AttributeError: with self.lock: if self.released: raise RuntimeError('the object is released') if (self.api.get(key) and self.api[key]['callable']): def proxy(*argv, **kwarg): self.channel_out.put({'name': key, 'argv': argv, 'namespace': self.ns, 'kwarg': kwarg}) return _handle(self.channel_in.get()) if key in self.api: proxy.__doc__ = self.api[key]['doc'] return proxy else: if key in ('stdin', 'stdout', 'stderr'): objns = ObjNS() objns.ns = key objns.api = self.api.get(key, {}).get('api', {}) objns.channel_out = self.channel_out objns.channel_in = self.channel_in objns.released = self.released objns.lock = self.lock return objns else: self.channel_out.put({'name': key, 'namespace': self.ns}) return _handle(self.channel_in.get()) @metaclass(MetaPopen) class NSPopen(ObjNS): ''' A proxy class to run `Popen()` object in some network namespace. Sample to run `ip ad` command in `nsname` network namespace:: nsp = NSPopen('nsname', ['ip', 'ad'], stdout=subprocess.PIPE) print(nsp.communicate()) nsp.wait() nsp.release() The `NSPopen` class was intended to be a drop-in replacement for the `Popen` class, but there are still some important differences. The `NSPopen` object implicitly spawns a child python process to be run in the background in a network namespace. The target process specified as the argument of the `NSPopen` will be started in its turn from this child. Thus all the fd numbers of the running `NSPopen` object are meaningless in the context of the main process. Trying to operate on them, one will get 'Bad file descriptor' in the best case or a system call working on a wrong file descriptor in the worst case. A possible solution would be to transfer file descriptors between the `NSPopen` object and the main process, but it is not implemented yet. The process' diagram for `NSPopen('test', ['ip', 'ad'])`:: +---------------------+ +--------------+ +------------+ | main python process |<--->| child python |<--->| netns test | | NSPopen() | | Popen() | | $ ip ad | +---------------------+ +--------------+ +------------+ As a workaround for the issue with file descriptors, some additional methods are available on file objects `stdin`, `stdout` and `stderr`. E.g., one can run fcntl calls:: from fcntl import F_GETFL from pyroute2 import NSPopen from subprocess import PIPE proc = NSPopen('test', ['my_program'], stdout=PIPE) flags = proc.stdout.fcntl(F_GETFL) In that way one can use `fcntl()`, `ioctl()`, `flock()` and `lockf()` calls. Another additional method is `release()`, which can be used to explicitly stop the proxy process and release all the resources. ''' def __init__(self, nsname, *argv, **kwarg): ''' The only differences from the `subprocess.Popen` init are: * `nsname` -- network namespace name * `flags` keyword argument All other arguments are passed directly to `subprocess.Popen`. Flags usage samples. Create a network namespace, if it doesn't exist yet:: import os nsp = NSPopen('nsname', ['command'], flags=os.O_CREAT) Create a network namespace only if it doesn't exist, otherwise fail and raise an exception:: import os nsp = NSPopen('nsname', ['command'], flags=os.O_CREAT | os.O_EXCL) ''' # create a child self.nsname = nsname if 'flags' in kwarg: self.flags = kwarg.pop('flags') else: self.flags = 0 self.channel_out = config.MpQueue() self.channel_in = config.MpQueue() self.lock = threading.Lock() self.released = False self.server = config.MpProcess(target=NSPopenServer, args=(self.nsname, self.flags, self.channel_out, self.channel_in, argv, kwarg)) # start the child and check the status self.server.start() response = self.channel_in.get() if isinstance(response, Exception): self.server.join() raise response else: atexit.register(self.release) def release(self): ''' Explicitly stop the proxy process and release all the resources. The `NSPopen` object can not be used after the `release()` call. ''' with self.lock: if self.released: return self.released = True self.channel_out.put({'name': 'release'}) self.channel_out.close() self.channel_in.close() self.server.join() def __dir__(self): return list(self.api.keys()) + ['release'] pyroute2-0.4.21/pyroute2/cli/0000775000175000017500000000000013152333366015652 5ustar peetpeet00000000000000pyroute2-0.4.21/pyroute2/cli/__init__.py0000664000175000017500000001717213152305337017770 0ustar peetpeet00000000000000from __future__ import print_function import re import sys import code import shlex import socket import getpass from pprint import pprint from pyroute2 import IPDB from pyroute2.common import basestring from pyroute2.ipdb.transactional import Transactional from pyroute2.ipdb.interfaces import Interface try: import pdb HAS_PDB = True except ImportError: HAS_PDB = False try: import readline HAS_READLINE = True except ImportError: HAS_READLINE = False class Console(code.InteractiveConsole): def __init__(self, stdout=None): global HAS_READLINE self.ipdb = IPDB() self.ptr = self.ipdb self.ptrname = None self.stack = [] self.matches = [] self.isatty = sys.stdin.isatty() self.prompt = '' self.stdout = stdout or sys.stdout self.set_prompt() code.InteractiveConsole.__init__(self) if HAS_READLINE: readline.parse_and_bind('tab: complete') readline.set_completer(self.completer) readline.set_completion_display_matches_hook(self.display) def write(self, text=''): self.lprint(text) def pprint(self, text=''): pprint(text, stream=self.stdout) self.stdout.flush() def lprint(self, text='', end='\n'): print(text, file=self.stdout, end=end) self.stdout.flush() def help(self): self.lprint("Built-in commands: \n" "debug\t-- run pdb (if installed)\n" "exit\t-- exit cli\n" "ls\t-- list current namespace\n" ".\t-- print the current object\n" ".. or ;\t-- one level up\n") def set_prompt(self, prompt=None): if self.isatty: if isinstance(self.ptr, Interface): self.prompt = 'if : %s > ' % (self.ptr.ifname) elif prompt is not None: self.prompt = '%s > ' % (prompt) else: self.prompt = '%s > ' % (self.ptr.__class__.__name__) self.prompt = '%s@%s : %s' % (getpass.getuser(), (socket .gethostname() .split('.')[0]), self.prompt) def convert(self, arg): if re.match('^[0-9]+$', arg): return int(arg) else: return arg def interact(self, readfunc=None): if readfunc is None: readfunc = self.raw_input if self.isatty: self.lprint("IPDB cli prototype") while True: try: cmd = readfunc(self.prompt) except: self.lprint() break # strip comments fbang = cmd.find('!') fhash = cmd.find('#') if fbang >= 0: cmd = cmd[:fbang] if cmd.find('#') >= 0: cmd = cmd[:fhash] # skip empty strings if not len(cmd.strip()): continue # calculate leading whitespaces lcmd = cmd.lstrip() lspaces = len(cmd) - len(lcmd) # strip all whitespaces cmd = cmd.strip() # compare spaces with self.ptr if not self.isatty: while self.stack and self.stack[-1][2] >= lspaces: # pop stack self.ptr, self.ptrname, spaces = self.stack.pop() # compare spaces if spaces == lspaces: break elif spaces < lspaces: self.lprint('indentation warning: <%s>' % cmd) break self.set_prompt(self.ptrname) if not cmd: continue elif cmd == 'debug': if HAS_PDB: pdb.set_trace() else: self.lprint("pdb is not available") elif cmd == 'exit': break elif cmd == 'ls': self.lprint(dir(self.ptr)) elif cmd == 'help': self.help() elif cmd == '.': self.lprint(repr(self.ptr)) elif cmd in ('..', ';'): if self.stack: self.ptr, self.ptrname, lspaces = self.stack.pop() self.set_prompt(self.ptrname) else: # parse the command line into tokens # # symbols .:/ etc. are needed to represent IP addresses # as whole tokens # # quotes should be stripped # parser = shlex.shlex(cmd) parser.wordchars += '.:/-+*' pre_tokens = list(parser) tokens = [] for token in pre_tokens: if token[0] == token[-1] and token[0] in ("\"'"): tokens.append(token[1:-1]) else: tokens.append(token) # an attribute obj = getattr(self.ptr, tokens[0], None) if obj is None: # try a kay try: obj = self.ptr[self.convert(cmd)] except Exception: self.lprint('object not found') continue if hasattr(obj, '__call__'): argv = [] kwarg = {} length = len(tokens) x = 1 while x < len(tokens): # is it a kwarg? if x < length - 2 and tokens[x + 1] == '=': kwarg[tokens[x]] = self.convert(tokens[x + 2]) x += 3 else: argv.append(self.convert(tokens[x])) x += 1 try: ret = obj(*argv, **kwarg) if ret and not isinstance(ret, (Transactional, IPDB)): self.pprint(ret) except: self.showtraceback() else: if isinstance(obj, (basestring, int, float)): # is it a simple attribute? if isinstance(self.ptr, Transactional) and \ len(tokens) > 1: # set it self.ptr[tokens[0]] = self.convert(tokens[1]) else: # or print it self.pprint(self.ptr[tokens[0]]) else: # otherwise change the context self.stack.append((self.ptr, self.ptrname, lspaces)) self.ptr = obj self.ptrname = tokens[0] self.set_prompt(tokens[0]) def completer(self, text, state): if state == 0: d = [x for x in dir(self.ptr) if x.startswith(text)] if isinstance(self.ptr, dict): keys = [str(y) for y in self.ptr.keys()] d.extend([x for x in keys if x.startswith(text)]) self.matches = d try: return self.matches[state] except: pass def display(self, line, matches, length): self.lprint() self.lprint(matches) self.lprint('%s%s' % (self.prompt, line), end='') if __name__ == '__main__': Console().interact() pyroute2-0.4.21/pyroute2/iproute.py0000664000175000017500000020051713152305337017146 0ustar peetpeet00000000000000# -*- coding: utf-8 -*- ''' 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'] Responses --------- The pyroute2 netlink socket implementation is agnostic to the particular netlink protocols, and always returns a list of messages as the response to a request sent to the kernel:: # this request returns one match eth0 = ipr.link_lookup(ifname='eth0') len(eth0) # -> 1, if exists, else 0 # but that one returns a set of up = ipr.link_lookup(operstate='UP') len(up) # -> k, where 0 <= k <= [interface count] Thus, always expect a list in the response, running any `IPRoute()` netlink request. NLMSG_ERROR responses ~~~~~~~~~~~~~~~~~~~~~ Some kernel subsystems return `NLMSG_ERROR` in the response to any request. It is OK as long as `nlmsg["header"]["error"] is None`. Otherwise an exception will be raised by the parser. So if instead of an exception you get a `NLMSG_ERROR` message, it means `error == 0`, the same as `$? == 0` in bash. How to work with messages ~~~~~~~~~~~~~~~~~~~~~~~~~ Every netlink message contains header, fields and NLAs (netlink attributes). Every NLA is a netlink message... (see "recursion"). And the library provides parsed messages according to this scheme. Every RTNL message contains: * `nlmsg['header']` -- parsed header * `nlmsg['attrs']` -- NLA chain (parsed on demand) * 0 .. k data fields, e.g. `nlmsg['flags']` etc. * `nlmsg.header` -- the header fields spec * `nlmsg.fields` -- the data fields spec * `nlmsg.nla_map` -- NLA spec An important parser feature is that NLAs are parsed on demand, when someone tries to access them. Otherwise the parser doesn't waste CPU cycles. The NLA chain is a list-like structure, not a dictionary. The netlink standard doesn't require NLAs to be unique within one message:: {'__align': (), 'attrs': [('IFLA_IFNAME', 'lo'), # [1] ('IFLA_TXQLEN', 1), ('IFLA_OPERSTATE', 'UNKNOWN'), ('IFLA_LINKMODE', 0), ('IFLA_MTU', 65536), ('IFLA_GROUP', 0), ('IFLA_PROMISCUITY', 0), ('IFLA_NUM_TX_QUEUES', 1), ('IFLA_NUM_RX_QUEUES', 1), ('IFLA_CARRIER', 1), ...], 'change': 0, 'event': 'RTM_NEWLINK', # [2] 'family': 0, 'flags': 65609, 'header': {'error': None, # [3] 'flags': 2, 'length': 1180, 'pid': 28233, 'sequence_number': 257, # [4] 'type': 16}, # [5] 'ifi_type': 772, 'index': 1} # [1] every NLA is parsed upon access # [2] this field is injected by the RTNL parser # [3] if not None, an exception will be raised # [4] more details in the netlink description # [5] 16 == RTM_NEWLINK To access fields:: msg['index'] == 1 To access one NLA:: msg.get_attr('IFLA_CARRIER') == 1 When the NLA with the specified name is not present in the chain, `get_attr()` returns `None`. To get the list of all NLAs of that name, use `get_attrs()`. A real example with NLA hierarchy, take notice of `get_attr()` and `get_attrs()` usage:: # for macvlan interfaces there may be several # IFLA_MACVLAN_MACADDR NLA provided, so use # get_attrs() to get all the list, not only # the first one (msg .get_attr('IFLA_LINKINFO') # one NLA .get_attr('IFLA_INFO_DATA') # one NLA .get_attrs('IFLA_MACVLAN_MACADDR')) # a list of Pls read carefully the message structure prior to start the coding. 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. API --- ''' import errno import types import logging from socket import AF_INET from socket import AF_INET6 from socket import AF_UNSPEC from pyroute2.config import AF_BRIDGE from pyroute2.netlink import NLMSG_DONE 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 import NLM_F_APPEND 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_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_NEWNEIGH from pyroute2.netlink.rtnl import RTM_GETNEIGH from pyroute2.netlink.rtnl import RTM_DELNEIGH from pyroute2.netlink.rtnl import RTM_SETLINK from pyroute2.netlink.rtnl import RTM_GETNEIGHTBL from pyroute2.netlink.rtnl import TC_H_ROOT from pyroute2.netlink.rtnl import rt_type from pyroute2.netlink.rtnl import rt_scope from pyroute2.netlink.rtnl import rt_proto from pyroute2.netlink.rtnl.req import IPLinkRequest from pyroute2.netlink.rtnl.req import IPBridgeRequest from pyroute2.netlink.rtnl.req import IPBrPortRequest from pyroute2.netlink.rtnl.req import IPRouteRequest from pyroute2.netlink.rtnl.req import IPRuleRequest from pyroute2.netlink.rtnl.tcmsg import plugins as tc_plugins from pyroute2.netlink.rtnl.tcmsg import tcmsg from pyroute2.netlink.rtnl.rtmsg import rtmsg from pyroute2.netlink.rtnl import ndmsg from pyroute2.netlink.rtnl.ndtmsg import ndtmsg from pyroute2.netlink.rtnl.fibmsg import fibmsg from pyroute2.netlink.rtnl.ifinfmsg import ifinfmsg from pyroute2.netlink.rtnl.ifaddrmsg import ifaddrmsg from pyroute2.netlink.rtnl.iprsocket import IPRSocket from pyroute2.netlink.rtnl.iprsocket import IPBatchSocket from pyroute2.netlink.rtnl.riprsocket import RawIPRSocket from pyroute2.common import AF_MPLS from pyroute2.common import basestring from pyroute2.common import getbroadcast from pyroute2.netlink.exceptions import NetlinkError DEFAULT_TABLE = 254 log = logging.getLogger(__name__) 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 RTNL API. Following classes use `IPRouteMixin`: * `IPRoute` -- RTNL API to the current network namespace * `NetNS` -- RTNL API to another network namespace * `IPBatch` -- RTNL compiler 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() # create an interface ipr.link('add', ifname='brx', kind='bridge') # lookup the index dev = ipr.link_lookup(ifname='brx')[0] # bring it down ipr.link('set', index=dev, state='down') # change the interface MAC address and rename it just for fun ipr.link('set', index=dev, address='00:11:22:33:44:55', ifname='br-ctrl') # add primary IP address ipr.addr('add', index=dev, address='10.0.0.1', mask=24, broadcast='10.0.0.255') # add secondary IP address ipr.addr('add', index=dev, address='10.0.0.2', mask=24, broadcast='10.0.0.255') # bring it up ipr.link('set', index=dev, state='up') ''' def _match(self, match, msgs): # filtered results f_ret = [] for msg in msgs: if hasattr(match, '__call__'): if match(msg): f_ret.append(msg) elif isinstance(match, dict): matches = [] for key in match: KEY = msg.name2nla(key) if isinstance(match[key], types.FunctionType): if msg.get(key) is not None: matches.append(match[key](msg.get(key))) elif msg.get_attr(KEY) is not None: matches.append(match[key](msg.get_attr(KEY))) else: matches.append(False) else: matches.append(msg.get(key) == match[key] or msg.get_attr(KEY) == match[key]) if all(matches): f_ret.append(msg) return f_ret # 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_vlans(self, **kwarg): ''' Dump available vlan info on bridge ports ''' # IFLA_EXT_MASK, extended info mask # # include/uapi/linux/rtnetlink.h # 1 << 0 => RTEXT_FILTER_VF # 1 << 1 => RTEXT_FILTER_BRVLAN # 1 << 2 => RTEXT_FILTER_BRVLAN_COMPRESSED # 1 << 3 => RTEXT_FILTER_SKIP_STATS # # maybe place it as mapping into ifinfomsg.py? # match = kwarg.get('match', None) or kwarg or None return self.link('dump', family=AF_BRIDGE, ext_mask=2, match=match) 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 [0] if links[0] == 'all': # compat syntax links = [0] if links[0] == 0: cmd = 'dump' else: cmd = 'get' for index in links: kwarg['index'] = index result.extend(self.link(cmd, **kwarg)) return result def get_neighbors(self, family=AF_UNSPEC): ''' Alias of `get_neighbours()`, deprecated. ''' log.warning('The `get_neighbors()` call is deprecated') log.warning('Use `get_neighbours() instead') return self.get_neighbours(family) def get_neighbours(self, family=AF_UNSPEC, match=None, **kwarg): ''' Dump ARP cache records. The `family` keyword sets the family for the request: e.g. `AF_INET` or `AF_INET6` for arp cache, `AF_BRIDGE` for fdb. If other keyword arguments not empty, they are used as filter. Also, one can explicitly set filter as a function with the `match` parameter. Examples:: # get neighbours on the 3rd link: ip.get_neighbours(ifindex=3) # get a particular record by dst: ip.get_neighbours(dst='172.16.0.1') # get fdb records: ip.get_neighbours(AF_BRIDGE) # and filter them by a function: ip.get_neighbours(AF_BRIDGE, match=lambda x: x['state'] == 2) ''' return self.neigh('dump', family=family, match=match or kwarg) def get_ntables(self, family=AF_UNSPEC): ''' Get neighbour tables ''' msg = ndtmsg() msg['family'] = family return self.nlm_request(msg, RTM_GETNEIGHTBL) def get_addr(self, family=AF_UNSPEC, match=None, **kwarg): ''' Dump addresses. If family is not specified, both AF_INET and AF_INET6 addresses will be dumped:: # get all addresses ip.get_addr() It is possible to apply filters on the results:: # get addresses for the 2nd interface ip.get_addr(index=2) # get addresses with IFA_LABEL == 'eth0' ip.get_addr(label='eth0') # get all the subnet addresses on the interface, identified # by broadcast address (should be explicitly specified upon # creation) ip.get_addr(index=2, broadcast='192.168.1.255') A custom predicate can be used as a filter:: ip.get_addr(match=lambda x: x['index'] == 1) ''' return self.addr((RTM_GETADDR, NLM_F_REQUEST | NLM_F_DUMP), family=family, match=match or kwarg) def get_rules(self, family=AF_UNSPEC, match=None, **kwarg): ''' Get all rules. By default return all rules. To explicitly request the IPv4 rules use `family=AF_INET`. Example:: ip.get_rules() # get all the rules for all families ip.get_rules(family=AF_INET6) # get only IPv6 rules ''' return self.rule((RTM_GETRULE, NLM_F_REQUEST | NLM_F_ROOT | NLM_F_ATOMIC), family=family, match=match or kwarg) def get_routes(self, family=AF_UNSPEC, match=None, **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 nkw = {} nkw['callback'] = kwarg.pop('callback', None) # get a particular route? if isinstance(kwarg.get('dst'), basestring): dlen = 32 if family == AF_INET else \ 128 if family == AF_INET6 else 0 msg_flags = NLM_F_REQUEST nkw['dst'] = kwarg.pop('dst') nkw['dst_len'] = kwarg.pop('dst_len', dlen) return self.route((RTM_GETROUTE, msg_flags), family=family, match=match or kwarg, **nkw) # 8<--------------------------------------------------------------- # 8<--------------------------------------------------------------- # # Shortcuts # 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 interface # # Obsoleted method. Use `link("add", ...)` instead. log.warning("link_create() is obsoleted, use link('add', ...)") return self.link('add', **IPLinkRequest(kwarg)) def link_up(self, index): # Link up. # # Obsoleted method. Use `link("set", ...)` instead. log.warning("link_up() is obsoleted, use link('set', ...)") return self.link('set', index=index, state='up') def link_down(self, index): # Link up. # # Obsoleted method. Use `link("set", ...)` instead. log.warning("link_down() is obsoleted, use link('set', ...)") return self.link('set', index=index, state='down') def link_rename(self, index, name): # Rename interface. # # Obsoleted method. Use `link("set", ...)` instead. log.warning("link_rename() is obsoleted, use link('set', ...)") return self.link('set', index=index, ifname=name) def link_remove(self, index): # Remove interface. # # Obsoleted method. Use `link("del", ...)` instead. log.warning("link_remove() is obsoleted, use link('del', ...)") return self.link('del', 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]] # 8<--------------------------------------------------------------- # 8<--------------------------------------------------------------- # # Shortcuts to flush RTNL objects # 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()`. ''' ret = [] match = kwarg.get('match') or kwarg def callback(msg): if msg['header']['type'] == NLMSG_DONE: # this message will pass to the get() return False if self._match(match, [msg]): # delete matching routes self.put(msg, msg_type=RTM_DELROUTE, msg_flags=NLM_F_REQUEST) return True nkw = {} nkw.update(kwarg) nkw['table'] = kwarg.get('table', 0) nkw['callback'] = callback self.get_routes(*argv, **nkw) return ret def flush_addr(self, *argv, **kwarg): ''' Flush IP addresses. Examples:: # flush all addresses on the interface with index 2: ipr.flush_addr(index=2) # flush all addresses with IFA_LABEL='eth0': ipr.flush_addr(label='eth0') ''' flags = NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL | NLM_F_REQUEST ret = [] for addr in self.get_addr(*argv, **kwarg): try: ret.append(self.nlm_request(addr, msg_type=RTM_DELADDR, msg_flags=flags)) except NetlinkError as e: if e.code != errno.EADDRNOTAVAIL: raise return ret def flush_rules(self, *argv, **kwarg): ''' Flush rules. Please keep in mind, that by default the function operates on **all** rules of **all** families. To work only on IPv4 rules, one should explicitly specify `family=AF_INET`. Examples:: # flush all IPv4 rule with priorities above 5 and below 32000 ipr.flush_rules(family=AF_INET, priority=lambda x: 5 < x < 32000) # flush all IPv6 rules that point to table 250: ipr.flush_rules(family=socket.AF_INET6, table=250) ''' flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL ret = [] for rule in self.get_rules(*argv, **kwarg): ret.append(self.nlm_request(rule, msg_type=RTM_DELRULE, msg_flags=flags)) return ret # 8<--------------------------------------------------------------- # 8<--------------------------------------------------------------- # # Extensions to low-level functions # def brport(self, command, **kwarg): ''' Set bridge port parameters. Example:: idx = ip.link_lookup(ifname='eth0') ip.brport("set", index=idx, unicast_flood=0, cost=200) ip.brport("show", index=idx) Possible keywords are NLA names for the `protinfo_bridge` class, without the prefix and in lower letters. ''' if (command in ('dump', 'show')) and ('match' not in kwarg): match = kwarg else: match = kwarg.pop('match', None) flags_dump = NLM_F_REQUEST | NLM_F_DUMP flags_req = NLM_F_REQUEST | NLM_F_ACK commands = {'set': (RTM_SETLINK, flags_req), 'dump': (RTM_GETLINK, flags_dump), 'show': (RTM_GETLINK, flags_dump)} (command, msg_flags) = commands.get(command, command) msg = ifinfmsg() if command == RTM_GETLINK: msg['index'] = kwarg.get('index', 0) else: msg['index'] = kwarg.pop('index', 0) msg['family'] = AF_BRIDGE protinfo = IPBrPortRequest(kwarg) msg['attrs'].append(('IFLA_PROTINFO', protinfo, 0x8000)) ret = self.nlm_request(msg, msg_type=command, msg_flags=msg_flags) if match is not None: return self._match(match, ret) else: return ret def vlan_filter(self, command, **kwarg): ''' Vlan filters is another approach to support vlans in Linux. Before vlan filters were introduced, there was only one way to bridge vlans: one had to create vlan interfaces and then add them as ports:: +------+ +----------+ net --> | eth0 | <--> | eth0.500 | <---+ +------+ +----------+ | v +------+ +-----+ net --> | eth1 | | br0 | +------+ +-----+ ^ +------+ +----------+ | net --> | eth2 | <--> | eth0.500 | <---+ +------+ +----------+ It means that one has to create as many bridges, as there were vlans. Vlan filters allow to bridge together underlying interfaces and create vlans already on the bridge:: # v500 label shows which interfaces have vlan filter +------+ v500 net --> | eth0 | <-------+ +------+ | v +------+ +-----+ +---------+ net --> | eth1 | <--> | br0 |<-->| br0v500 | +------+ +-----+ +---------+ ^ +------+ v500 | net --> | eth2 | <-------+ +------+ In this example vlan 500 will be allowed only on ports `eth0` and `eth2`, though all three eth nics are bridged. Some example code:: # create bridge ip.link("add", ifname="br0", kind="bridge") # attach a port ip.link("set", index=ip.link_lookup(ifname="eth0")[0], master=ip.link_lookup(ifname="br0")[0]) # set vlan filter ip.vlan_filter("add", index=ip.link_lookup(ifname="eth0")[0], vlan_info={"vid": 500}) # create vlan interface on the bridge ip.link("add", ifname="br0v500", kind="vlan", link=ip.link_lookup(ifname="br0")[0], vlan_id=500) # set all UP ip.link("set", index=ip.link_lookup(ifname="br0")[0], state="up") ip.link("set", index=ip.link_lookup(ifname="br0v500")[0], state="up") ip.link("set", index=ip.link_lookup(ifname="eth0")[0], state="up") # set IP address ip.addr("add", index=ip.link_lookup(ifname="br0v500")[0], address="172.16.5.2", mask=24) Now all the traffic to the network 172.16.5.2/24 will go to vlan 500 only via ports that have such vlan filter. Required arguments for `vlan_filter()` -- `index` and `vlan_info`. Vlan info struct:: {"vid": uint16, "flags": uint16} More details: * kernel:Documentation/networking/switchdev.txt * pyroute2.netlink.rtnl.ifinfmsg:... vlan_info One can specify `flags` as int or as a list of flag names: * `master` == 0x1 * `pvid` == 0x2 * `untagged` == 0x4 * `range_begin` == 0x8 * `range_end` == 0x10 * `brentry` == 0x20 E.g.:: {"vid": 20, "flags": ["pvid", "untagged"]} # is equal to {"vid": 20, "flags": 6} Commands: **add** Add vlan filter to a bridge port. Example:: ip.vlan_filter("add", index=2, vlan_info={"vid": 200}) **del** Remove vlan filter from a bridge port. Example:: ip.vlan_filter("del", index=2, vlan_info={"vid": 200}) ''' flags_req = NLM_F_REQUEST | NLM_F_ACK commands = {'add': (RTM_SETLINK, flags_req), 'del': (RTM_DELLINK, flags_req)} kwarg['family'] = AF_BRIDGE kwarg['kwarg_filter'] = IPBridgeRequest (command, flags) = commands.get(command, command) return self.link((command, flags), **kwarg) def fdb(self, command, **kwarg): ''' Bridge forwarding database management. More details: * kernel:Documentation/networking/switchdev.txt * pyroute2.netlink.rtnl.ndmsg **add** Add a new FDB record. Works in the same way as ARP cache management, but some additional NLAs can be used:: # simple FDB record # ip.fdb('add', ifindex=ip.link_lookup(ifname='br0')[0], lladdr='00:11:22:33:44:55', dst='10.0.0.1') # specify vlan # NB: vlan should exist on the device, use # `vlan_filter()` # ip.fdb('add', ifindex=ip.link_lookup(ifname='br0')[0], lladdr='00:11:22:33:44:55', dst='10.0.0.1', vlan=200) # specify vxlan id and port # NB: works only for vxlan devices, use # `link("add", kind="vxlan", ...)` # # if port is not specified, the default one is used # by the kernel. # # if vni (vxlan id) is equal to the device vni, # the kernel doesn't report it back # ip.fdb('add', ifindex=ip.link_lookup(ifname='vx500')[0] lladdr='00:11:22:33:44:55', dst='10.0.0.1', port=5678, vni=600) **append** Append a new FDB record. The same syntax as for **add**. **del** Remove an existing FDB record. The same syntax as for **add**. **dump** Dump all the FDB records. If any `**kwarg` is provided, results will be filtered:: # dump all the records ip.fdb('dump') # show only specific lladdr, dst, vlan etc. ip.fdb('dump', lladdr='00:11:22:33:44:55') ip.fdb('dump', dst='10.0.0.1') ip.fdb('dump', vlan=200) ''' kwarg['family'] = AF_BRIDGE # nud -> state if 'nud' in kwarg: kwarg['state'] = kwarg.pop('nud') if (command in ('add', 'del', 'append')) and \ not (kwarg.get('state', 0) & ndmsg.states['noarp']): # state must contain noarp in add / del / append kwarg['state'] = kwarg.pop('state', 0) | ndmsg.states['noarp'] # other assumptions if not kwarg.get('state', 0) & (ndmsg.states['permanent'] | ndmsg.states['reachable']): # permanent (default) or reachable kwarg['state'] |= ndmsg.states['permanent'] if not kwarg.get('flags', 0) & (ndmsg.flags['self'] | ndmsg.flags['master']): # self (default) or master kwarg['flags'] = kwarg.get('flags', 0) | ndmsg.flags['self'] # return self.neigh(command, **kwarg) # 8<--------------------------------------------------------------- # # General low-level configuration methods # def neigh(self, command, **kwarg): ''' Neighbours operations, same as `ip neigh` or `bridge fdb` **add** Add a neighbour record, e.g.:: # add a permanent record on veth0 idx = ip.link_lookup(ifname='veth0')[0] ip.neigh('add', dst='172.16.45.1', lladdr='00:11:22:33:44:55', ifindex=ip.link_lookup(ifname='veth0')[0] state=ndmsg.states['permanent']) **set** Set an existing record or create a new one, if it doesn't exist. **change** Change an existing record or fail, if it doesn't exist. **del** Delete an existing record. **dump** Dump all the records in the NDB. ''' if (command == 'dump') and ('match' not in kwarg): match = kwarg else: match = kwarg.pop('match', None) flags_dump = NLM_F_REQUEST | NLM_F_DUMP flags_base = NLM_F_REQUEST | NLM_F_ACK flags_make = flags_base | NLM_F_CREATE | NLM_F_EXCL flags_append = flags_base | NLM_F_CREATE | NLM_F_APPEND flags_change = flags_base | NLM_F_REPLACE flags_replace = flags_change | NLM_F_CREATE commands = {'add': (RTM_NEWNEIGH, flags_make), 'set': (RTM_NEWNEIGH, flags_replace), 'replace': (RTM_NEWNEIGH, flags_replace), 'change': (RTM_NEWNEIGH, flags_change), 'del': (RTM_DELNEIGH, flags_make), 'remove': (RTM_DELNEIGH, flags_make), 'delete': (RTM_DELNEIGH, flags_make), 'dump': (RTM_GETNEIGH, flags_dump), 'append': (RTM_NEWNEIGH, flags_append)} (command, flags) = commands.get(command, command) if 'nud' in kwarg: kwarg['state'] = kwarg.pop('nud') msg = ndmsg.ndmsg() for field in msg.fields: msg[field[0]] = kwarg.pop(field[0], 0) msg['family'] = msg['family'] or AF_INET msg['attrs'] = [] # fix nud kwarg if isinstance(msg['state'], basestring): msg['state'] = ndmsg.states_a2n(msg['state']) for key in kwarg: nla = ndmsg.ndmsg.name2nla(key) if kwarg[key] is not None: msg['attrs'].append([nla, kwarg[key]]) ret = self.nlm_request(msg, msg_type=command, msg_flags=flags) if match is not None: return self._match(match, ret) else: return ret def link(self, command, **kwarg): ''' Link operations. Keywords to set up ifinfmsg fields: * index -- interface index * family -- AF_BRIDGE for bridge operations, otherwise 0 * flags -- device flags * change -- change mask All other keywords will be translated to NLA names, e.g. `mtu -> IFLA_MTU`, `af_spec -> IFLA_AF_SPEC` etc. You can provide a complete NLA structure or let filters do it for you. E.g., these pairs show equal statements:: # set device MTU ip.link("set", index=x, mtu=1000) ip.link("set", index=x, IFLA_MTU=1000) # add vlan device ip.link("add", ifname="test", kind="dummy") ip.link("add", ifname="test", IFLA_LINKINFO={'attrs': [['IFLA_INFO_KIND', 'dummy']]}) Filters are implemented in the `pyroute2.netlink.rtnl.req` module. You can contribute your own if you miss shortcuts. Commands: **add** To create an interface, one should specify the interface kind:: ip.link("add", ifname="test", kind="dummy") The kind can be any of those supported by kernel. It can be `dummy`, `bridge`, `bond` etc. On modern kernels one can specify even interface index:: ip.link("add", ifname="br-test", kind="bridge", index=2345) Specific type notes: ► gre Create GRE tunnel:: ip.link("add", ifname="grex", kind="gre", gre_local="172.16.0.1", gre_remote="172.16.0.101", gre_ttl=16) The keyed GRE requires explicit iflags/oflags specification:: ip.link("add", ifname="grex", kind="gre", gre_local="172.16.0.1", gre_remote="172.16.0.101", gre_ttl=16, gre_ikey=10, gre_okey=10, gre_iflags=32, gre_oflags=32) Support for GRE over IPv6 is also included; use `kind=ip6gre` and `ip6gre_` as the prefix for its values. ► macvlan Macvlan interfaces act like VLANs within OS. The macvlan driver provides an ability to add several MAC addresses on one interface, where every MAC address is reflected with a virtual interface in the system. In some setups macvlan interfaces can replace bridge interfaces, providing more simple and at the same time high-performance solution:: ip.link("add", ifname="mvlan0", kind="macvlan", link=ip.link_lookup(ifname="em1")[0], macvlan_mode="private").commit() Several macvlan modes are available: "private", "vepa", "bridge", "passthru". Ususally the default is "vepa". ► macvtap Almost the same as macvlan, but creates also a character tap device:: ip.link("add", ifname="mvtap0", kind="macvtap", link=ip.link_lookup(ifname="em1")[0], macvtap_mode="vepa").commit() Will create a device file `"/dev/tap%s" % index` ► tuntap Possible `tuntap` keywords: - `mode` — "tun" or "tap" - `uid` — integer - `gid` — integer - `ifr` — dict of tuntap flags (see ifinfmsg:... tuntap_data) Create a tap interface:: ip.link("add", ifname="tap0", kind="tuntap", mode="tap") Tun/tap interfaces are created using `ioctl()`, but the library provides a transparent way to manage them using netlink API. ► veth To properly create `veth` interface, one should specify `peer` also, since `veth` interfaces are created in pairs:: ip.link("add", ifname="v1p0", kind="veth", peer="v1p1") ► vlan VLAN interfaces require additional parameters, `vlan_id` and `link`, where `link` is a master interface to create VLAN on:: ip.link("add", ifname="v100", kind="vlan", link=ip.link_lookup(ifname="eth0")[0], vlan_id=100) There is a possibility to create also 802.1ad interfaces:: # create external vlan 802.1ad, s-tag ip.link("add", ifname="v100s", kind="vlan", link=ip.link_lookup(ifname="eth0")[0], vlan_id=100, vlan_protocol=0x88a8) # create internal vlan 802.1q, c-tag ip.link("add", ifname="v100c", kind="vlan", link=ip.link_lookup(ifname="v100s")[0], vlan_protocol=0x8100) ► vrf VRF interfaces (see linux/Documentation/networking/vrf.txt):: ip.link("add", ifname="vrf-foo", kind="vrf", vrf_table=42) ► vxlan VXLAN interfaces are like VLAN ones, but require a bit more parameters:: ip.link("add", ifname="vx101", kind="vxlan", vxlan_link=ip.link_lookup(ifname="eth0")[0], vxlan_id=101, vxlan_group='239.1.1.1', vxlan_ttl=16) All possible vxlan parameters are listed in the module `pyroute2.netlink.rtnl.ifinfmsg:... vxlan_data`. **set** Set interface attributes:: # get interface index x = ip.link_lookup(ifname="eth0")[0] # put link down ip.link("set", index=x, state="down") # rename and set MAC addr ip.link("set", index=x, address="00:11:22:33:44:55", name="bala") # set MTU and TX queue length ip.link("set", index=x, mtu=1000, txqlen=2000) # bring link up ip.link("set", index=x, state="up") Keyword "state" is reserved. State can be "up" or "down", it is a shortcut:: state="up": flags=1, mask=1 state="down": flags=0, mask=0 **del** Destroy the interface:: ip.link("del", index=ip.link_lookup(ifname="dummy0")[0]) **dump** Dump info for all interfaces **get** Get specific interface info:: ip.link("get", index=ip.link_lookup(ifname="br0")[0]) **vlan-add** **vlan-del** These command names are confusing and thus are deprecated. Use `IPRoute.vlan_filter()`. ''' if (command == 'dump') and ('match' not in kwarg): match = kwarg else: match = kwarg.pop('match', None) if command[:4] == 'vlan': log.warning('vlan filters are managed via `vlan_filter()`') log.warning('this compatibility hack will be removed soon') return self.vlan_filter(command[5:], **kwarg) flags_dump = NLM_F_REQUEST | NLM_F_DUMP flags_req = NLM_F_REQUEST | NLM_F_ACK flags_create = flags_req | NLM_F_CREATE | NLM_F_EXCL commands = {'set': (RTM_SETLINK, flags_create), 'add': (RTM_NEWLINK, flags_create), 'del': (RTM_DELLINK, flags_create), 'remove': (RTM_DELLINK, flags_create), 'delete': (RTM_DELLINK, flags_create), 'dump': (RTM_GETLINK, flags_dump), 'get': (RTM_GETLINK, NLM_F_REQUEST)} msg = ifinfmsg() # ifinfmsg fields # # ifi_family # ifi_type # ifi_index # ifi_flags # ifi_change # msg['family'] = kwarg.pop('family', 0) lrq = kwarg.pop('kwarg_filter', IPLinkRequest) (command, msg_flags) = commands.get(command, command) # index msg['index'] = kwarg.pop('index', 0) # flags flags = kwarg.pop('flags', 0) or 0 # change mask = kwarg.pop('mask', 0) or kwarg.pop('change', 0) or 0 # UP/DOWN shortcut 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 # apply filter kwarg = lrq(kwarg) # attach NLA for key in kwarg: nla = type(msg).name2nla(key) if kwarg[key] is not None: msg['attrs'].append([nla, kwarg[key]]) ret = self.nlm_request(msg, msg_type=command, msg_flags=msg_flags) if match is not None: return self._match(match, ret) else: return ret def addr(self, command, index=None, address=None, mask=None, family=None, scope=None, match=None, **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 * \*\*kwarg -- any ifaddrmsg field or NLA Later the method signature will be changed to:: def addr(self, command, match=None, **kwarg): # the method body So only keyword arguments (except of the command) will be accepted. The reason for this change is an unification of API. Example:: idx = 62 ip.addr('add', index=idx, address='10.0.0.1', mask=24) ip.addr('add', index=idx, address='10.0.0.2', mask=24) With more NLAs:: # explicitly set broadcast address ip.addr('add', index=idx, address='10.0.0.3', broadcast='10.0.0.255', prefixlen=24) # make the secondary address visible to ifconfig: add label ip.addr('add', index=idx, address='10.0.0.4', broadcast='10.0.0.255', prefixlen=24, label='eth0:1') Configure p2p address on an interface:: ip.addr('add', index=idx, address='10.1.1.2', mask=24, local='10.1.1.1') ''' flags_create = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL commands = {'add': (RTM_NEWADDR, flags_create), 'del': (RTM_DELADDR, flags_create), 'remove': (RTM_DELADDR, flags_create), 'delete': (RTM_DELADDR, flags_create)} (command, flags) = commands.get(command, command) # fetch args index = index or kwarg.pop('index', 0) family = family or kwarg.pop('family', None) prefixlen = mask or kwarg.pop('mask', 0) or kwarg.pop('prefixlen', 0) scope = scope or kwarg.pop('scope', 0) # move address to kwarg # FIXME: add deprecation notice if address: kwarg['address'] = address # try to guess family, if it is not forced if kwarg.get('address') and family is None: if address.find(":") > -1: family = AF_INET6 mask = mask or 128 else: family = AF_INET mask = mask or 32 # setup the message msg = ifaddrmsg() msg['index'] = index msg['family'] = family or 0 msg['prefixlen'] = prefixlen msg['scope'] = scope # inject IFA_LOCAL, if family is AF_INET and IFA_LOCAL is not set if family == AF_INET and \ kwarg.get('address') and \ kwarg.get('local') is None: kwarg['local'] = kwarg['address'] # patch broadcast, if needed if kwarg.get('broadcast') is True: kwarg['broadcast'] = getbroadcast(address, mask, family) # work on NLA for key in kwarg: nla = ifaddrmsg.name2nla(key) if kwarg[key] is not None: msg['attrs'].append([nla, kwarg[key]]) ret = self.nlm_request(msg, msg_type=command, msg_flags=flags, terminate=lambda x: x['header']['type'] == NLMSG_ERROR) if match: return self._match(match, ret) else: return ret def tc(self, command, kind=None, index=0, 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 Target notice: if your target is a class/qdisc that applies an algorithm that can only apply to upstream traffic profile, but your keys variable explicitly references a match that is only relevant for upstream traffic, the kernel will reject the filter. Unless you're dealing with devices like IMQs 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() flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL ip.tc((RTM_NEWQDISC, flags), "sfq", 1) It should be noted that "change", "change-class" and "change-filter" work like "replace", "replace-class" and "replace-filter", except they will fail if the node doesn't exist (while it would have been created by "replace"). This is not the same behaviour as with "tc" where "change" can be used to modify the value of some options while leaving the others unchanged. However, as not all entities support this operation, we believe the "change" commands as implemented here are more useful. Also available "modules" (returns tc plugins dict) and "help" commands:: help(ip.tc("modules")["htb"]) print(ip.tc("help", "htb")) ''' if command == 'modules': return tc_plugins if command == 'help': p = tc_plugins.get(kind) if p is not None and hasattr(p, '__doc__'): return p.__doc__ else: return 'No help available' flags_base = NLM_F_REQUEST | NLM_F_ACK flags_make = flags_base | NLM_F_CREATE | NLM_F_EXCL flags_change = flags_base | NLM_F_REPLACE flags_replace = flags_change | NLM_F_CREATE commands = {'add': (RTM_NEWQDISC, flags_make), 'del': (RTM_DELQDISC, flags_make), 'remove': (RTM_DELQDISC, flags_make), 'delete': (RTM_DELQDISC, flags_make), 'change': (RTM_NEWQDISC, flags_change), 'replace': (RTM_NEWQDISC, flags_replace), 'add-class': (RTM_NEWTCLASS, flags_make), 'del-class': (RTM_DELTCLASS, flags_make), 'change-class': (RTM_NEWTCLASS, flags_change), 'replace-class': (RTM_NEWTCLASS, flags_replace), 'add-filter': (RTM_NEWTFILTER, flags_make), 'del-filter': (RTM_DELTFILTER, flags_make), 'change-filter': (RTM_NEWTFILTER, flags_change), 'replace-filter': (RTM_NEWTFILTER, flags_replace)} if isinstance(command, int): command = (command, flags_make) command, flags = commands.get(command, command) 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 in tc_plugins: p = tc_plugins[kind] msg['parent'] = kwarg.pop('parent', getattr(p, 'parent', 0)) if hasattr(p, 'fix_msg'): p.fix_msg(msg, kwarg) if kwarg: if command in (RTM_NEWTCLASS, RTM_DELTCLASS): opts = p.get_class_parameters(kwarg) else: opts = p.get_parameters(kwarg) else: msg['parent'] = kwarg.get('parent', TC_H_ROOT) if kind is not None: msg['attrs'].append(['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, **kwarg): ''' Route operations. Keywords to set up rtmsg fields: * dst_len, src_len -- destination and source mask(see `dst` below) * tos -- type of service * table -- routing table * proto -- `redirect`, `boot`, `static` (see `rt_proto`) * scope -- routing realm * type -- `unicast`, `local`, etc. (see `rt_type`) `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` * iif -- incoming traffic interface * oif -- outgoing traffic interface etc. One can specify mask not as `dst_len`, but as a part of `dst`, e.g.: `dst="10.0.0.0/24"`. Commands: **add** Example:: ip.route("add", dst="10.0.0.0/24", gateway="192.168.0.1") It is possible to set also route metrics. There are two ways to do so. The first is to use 'raw' NLA notation:: ip.route("add", dst="10.0.0.0", mask=24, gateway="192.168.0.1", metrics={"attrs": [["RTAX_MTU", 1400], ["RTAX_HOPLIMIT", 16]]}) The second way is to use shortcuts, provided by `IPRouteRequest` class, which is applied to `**kwarg` automatically:: ip.route("add", dst="10.0.0.0/24", gateway="192.168.0.1", metrics={"mtu": 1400, "hoplimit": 16}) ... More `route()` examples. Blackhole route:: ip.route("add", dst="10.0.0.0/24", type="blackhole") Multipath route:: ip.route("add", dst="10.0.0.0/24", multipath=[{"gateway": "192.168.0.1", "hops": 2}, {"gateway": "192.168.0.2", "hops": 1}, {"gateway": "192.168.0.3"}]) MPLS lwtunnel on eth0:: idx = ip.link_lookup(ifname='eth0')[0] ip.route("add", dst="10.0.0.0/24", oif=idx, encap={"type": "mpls", "labels": "200/300"}) MPLS multipath:: idx = ip.link_lookup(ifname='eth0')[0] ip.route("add", dst="10.0.0.0/24", table=20, multipath=[{"gateway": "192.168.0.1", "encap": {"type": "mpls", "labels": 200}}, {"ifindex": idx, "encap": {"type": "mpls", "labels": 300}}]) MPLS target can be int, string, dict or list:: "labels": 300 # simple label "labels": "300" # the same "labels": (200, 300) # stacked "labels": "200/300" # the same # explicit label definition "labels": {"bos": 1, "label": 300, "tc": 0, "ttl": 16} **change**, **replace** Commands `change` and `replace` have the same meanings, as in ip-route(8): `change` modifies only existing route, while `replace` creates a new one, if there is no such route yet. **del** Remove the route. The same syntax as for **add**. **get** Get route by spec. **dump** Dump all routes. ''' # 8<---------------------------------------------------- # FIXME # flags should be moved to some more general place flags_dump = NLM_F_DUMP | NLM_F_REQUEST flags_base = NLM_F_REQUEST | NLM_F_ACK flags_make = flags_base | NLM_F_CREATE | NLM_F_EXCL flags_change = flags_base | NLM_F_REPLACE flags_replace = flags_change | NLM_F_CREATE # 8<---------------------------------------------------- # transform kwarg if command in ('add', 'set', 'replace', 'change'): kwarg['proto'] = kwarg.get('proto', 'static') or 'static' kwarg['type'] = kwarg.get('type', 'unicast') or 'unicast' kwarg = IPRouteRequest(kwarg) if command in ('dump', 'show'): match = kwarg else: match = kwarg.pop('match', None) callback = kwarg.pop('callback', None) commands = {'add': (RTM_NEWROUTE, flags_make), 'set': (RTM_NEWROUTE, flags_replace), 'replace': (RTM_NEWROUTE, flags_replace), 'change': (RTM_NEWROUTE, flags_change), 'del': (RTM_DELROUTE, flags_make), 'remove': (RTM_DELROUTE, flags_make), 'delete': (RTM_DELROUTE, flags_make), 'get': (RTM_GETROUTE, NLM_F_REQUEST), 'show': (RTM_GETROUTE, flags_dump), 'dump': (RTM_GETROUTE, flags_dump)} (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.pop('family', AF_INET) msg['scope'] = kwarg.pop('scope', rt_scope['universe']) msg['dst_len'] = kwarg.pop('dst_len', None) or kwarg.pop('mask', 0) msg['src_len'] = kwarg.pop('src_len', 0) msg['tos'] = kwarg.pop('tos', 0) msg['flags'] = kwarg.pop('flags', 0) msg['type'] = kwarg.pop('type', rt_type['unspec']) msg['proto'] = kwarg.pop('proto', rt_proto['unspec']) msg['attrs'] = [] if msg['family'] == AF_MPLS: for key in tuple(kwarg): if key not in ('dst', 'newdst', 'via', 'multipath', 'oif'): kwarg.pop(key) for key in kwarg: nla = rtmsg.name2nla(key) if kwarg[key] is not None: msg['attrs'].append([nla, kwarg[key]]) # fix IP family, if needed if msg['family'] == AF_UNSPEC: if key in ('dst', 'src', 'gateway', 'prefsrc', 'newdst') \ and isinstance(kwarg[key], basestring): msg['family'] = AF_INET6 if kwarg[key].find(':') >= 0 \ else AF_INET elif key == 'multipath' and len(kwarg[key]) > 0: hop = kwarg[key][0] attrs = hop.get('attrs', []) for attr in attrs: if attr[0] == 'RTA_GATEWAY': msg['family'] = AF_INET6 if \ attr[1].find(':') >= 0 else AF_INET break ret = self.nlm_request(msg, msg_type=command, msg_flags=flags, callback=callback) if match: return self._match(match, ret) else: return ret def rule(self, command, *argv, **kwarg): ''' Rule operations - command — add, delete - table — 0 < table id < 253 - priority — 0 < rule's priority < 32766 - action — type of rule, default 'FR_ACT_NOP' (see fibmsg.py) - 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 - iifname — Input interface for Interface Based (Policy Based) routing's rule - oifname — Output interface for Interface Based (Policy Based) routing's rule All packets route via table 10:: # 32000: from all lookup 10 # ... ip.rule('add', table=10, priority=32000) Default action:: # 32001: from all lookup 11 unreachable # ... iproute.rule('add', table=11, priority=32001, action='FR_ACT_UNREACHABLE') Use source address to choose a routing table:: # 32004: from 10.64.75.141 lookup 14 # ... iproute.rule('add', table=14, priority=32004, src='10.64.75.141') Use dst address to choose a routing table:: # 32005: from 10.64.75.141/24 lookup 15 # ... iproute.rule('add', table=15, priority=32005, dst='10.64.75.141', dst_len=24) Match fwmark:: # 32006: from 10.64.75.141 fwmark 0xa lookup 15 # ... iproute.rule('add', table=15, priority=32006, dst='10.64.75.141', fwmark=10) ''' flags_base = NLM_F_REQUEST | NLM_F_ACK flags_make = flags_base | NLM_F_CREATE | NLM_F_EXCL flags_dump = NLM_F_REQUEST | NLM_F_ROOT | NLM_F_ATOMIC commands = {'add': (RTM_NEWRULE, flags_make), 'del': (RTM_DELRULE, flags_make), 'remove': (RTM_DELRULE, flags_make), 'delete': (RTM_DELRULE, flags_make), 'dump': (RTM_GETRULE, flags_dump)} if isinstance(command, int): command = (command, flags_make) command, flags = commands.get(command, command) if argv: # this code block will be removed in some release log.error('rule(): positional parameters are deprecated') names = ['table', 'priority', 'action', 'family', 'src', 'src_len', 'dst', 'dst_len', 'fwmark', 'iifname', 'oifname'] kwarg.update(dict(zip(names, argv))) kwarg = IPRuleRequest(kwarg) msg = fibmsg() table = kwarg.get('table', 0) msg['table'] = table if table <= 255 else 252 for key in ('family', 'src_len', 'dst_len', 'action', 'tos', 'flags'): msg[key] = kwarg.pop(key, 0) msg['attrs'] = [] for key in kwarg: nla = fibmsg.name2nla(key) if kwarg[key] is not None: msg['attrs'].append([nla, kwarg[key]]) ret = self.nlm_request(msg, msg_type=command, msg_flags=flags) if 'match' in kwarg: return self._match(kwarg['match'], ret) else: return ret # 8<--------------------------------------------------------------- class IPBatch(IPRouteMixin, IPBatchSocket): ''' Netlink requests compiler. Does not send any requests, but instead stores them in the internal binary buffer. The contents of the buffer can be used to send batch requests, to test custom netlink parsers and so on. Uses `IPRouteMixin` and provides all the same API as normal `IPRoute` objects:: # create the batch compiler ipb = IPBatch() # compile requests into the internal buffer ipb.link("add", index=550, ifname="test", kind="dummy") ipb.link("set", index=550, state="up") ipb.addr("add", index=550, address="10.0.0.2", mask=24) # save the buffer data = ipb.batch # reset the buffer ipb.reset() ... # send the buffer IPRoute().sendto(data, (0, 0)) ''' pass class IPRoute(IPRouteMixin, IPRSocket): ''' Public class that provides RTNL API to the current network namespace. ''' pass class RawIPRoute(IPRouteMixin, RawIPRSocket): ''' The same as `IPRoute`, but does not use the netlink proxy. Thus it can not manage e.g. tun/tap interfaces. ''' pass pyroute2-0.4.21/pyroute2/ipdb/0000775000175000017500000000000013152333366016021 5ustar peetpeet00000000000000pyroute2-0.4.21/pyroute2/ipdb/interfaces.py0000664000175000017500000014716313152331252020522 0ustar peetpeet00000000000000import time import errno import traceback from socket import AF_INET from socket import AF_INET6 from socket import inet_ntop from socket import inet_pton from socket import error as socket_error from pyroute2 import config from pyroute2.config import AF_BRIDGE from pyroute2.common import basestring from pyroute2.common import dqn2int from pyroute2.common import View from pyroute2.common import Dotkeys from pyroute2.netlink import rtnl from pyroute2.netlink import NLM_F_ACK from pyroute2.netlink import NLM_F_REQUEST from pyroute2.netlink.exceptions import NetlinkError from pyroute2.netlink.rtnl import RTM_NEWLINK 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 with_transaction from pyroute2.ipdb.transactional import SYNC_TIMEOUT from pyroute2.ipdb.linkedset import LinkedSet from pyroute2.ipdb.exceptions import CreateException from pyroute2.ipdb.exceptions import CommitException from pyroute2.ipdb.exceptions import PartialCommitException groups = rtnl.RTNLGRP_LINK |\ rtnl.RTNLGRP_NEIGH |\ rtnl.RTNLGRP_IPV4_IFADDR |\ rtnl.RTNLGRP_IPV6_IFADDR def _get_data_fields(): ret = [] for data in ('bridge', 'bond', 'tuntap', 'vxlan', 'gre', 'ip6gre', 'macvlan', 'macvtap', 'ipvlan', 'vrf'): msg = ifinfmsg.ifinfo.data_map[data] if getattr(msg, 'prefix', None) is not None: ret += [msg.nla2name(i[0]) for i in msg.nla_map] else: ret += [ifinfmsg.nla2name(i[0]) for i in msg.nla_map] return ret def _br_time_check(x, y): return abs(x - y) < 5 class Interface(Transactional): ''' Objects of this class represent network interface and all related objects: * addresses * (todo) neighbours * (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, 'br_hello_time': _br_time_check, 'br_max_age': _br_time_check, 'br_ageing_time': _br_time_check, 'br_forward_delay': _br_time_check, 'br_mcast_membership_intvl': _br_time_check, 'br_mcast_querier_intvl': _br_time_check, 'br_mcast_query_intvl': _br_time_check, 'br_mcast_query_response_intvl': _br_time_check, 'br_mcast_startup_query_intvl': _br_time_check} _virtual_fields = ['ipdb_scope', 'ipdb_priority', 'vlans', 'ipaddr', 'ports', 'vlan_flags', 'net_ns_fd', 'net_ns_pid'] _fields = [ifinfmsg.nla2name(i[0]) for i in ifinfmsg.nla_map] for name in ('bridge_data', 'bridge_slave_data'): data = getattr(ifinfmsg.ifinfo, name) _fields.extend([ifinfmsg.nla2name(i[0]) for i in data.nla_map]) _fields.append('index') _fields.append('flags') _fields.append('mask') _fields.append('change') _fields.append('kind') _fields.append('peer') _fields.append('vlan_id') _fields.append('vlan_protocol') _fields.append('bond_mode') _fields.extend(_get_data_fields()) _fields.extend(_virtual_fields) 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', 'protinfo', 'af_spec', 'attrs', 'event', 'map', 'stats', 'stats64', 'change', '__align') self.ingress = None self.egress = None self.nlmsg = None self.errors = [] self.partial = False self._exception = None self._deferred_link = None self._tb = None self._linked_sets.add('ipaddr') self._linked_sets.add('ports') self._linked_sets.add('vlans') self._freeze = None self._delay_add_port = set() self._delay_del_port = set() # 8<----------------------------------- # local setup: direct state is required with self._direct_state: for i in ('change', 'mask'): del self[i] self['ipaddr'] = self.ipdb._ipaddr_set() self['ports'] = LinkedSet() self['vlans'] = LinkedSet() self['ipdb_priority'] = 0 # 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 detach(self): self.ipdb.interfaces._detach(self['ifname'], self['index'], self.nlmsg) return self def freeze(self): if self._freeze is not None: raise RuntimeError("the interface is frozen already") dump = self.pick() def cb(ipdb, msg, action): if msg.get('index', -1) == dump['index']: try: # important: that's a rollback, so do not # try to revert changes in the case of failure self.commit(transaction=dump, commit_phase=2, commit_mask=2) except Exception: pass self._freeze = self.ipdb.register_callback(cb) return self def unfreeze(self): self.ipdb.unregister_callback(self._freeze) self._freeze = None return self def load(self, data): ''' Load the data from a dictionary to an existing transaction. Requires `commit()` call, or must be called from within a `with` statement. Sample:: data = json.loads(...) with ipdb.interfaces['dummy1'] as i: i.load(data) Sample, mode `explicit:: data = json.loads(...) i = ipdb.interfaces['dummy1'] i.begin() i.load(data) i.commit() ''' for key in data: if data[key] is None: continue if key == 'ipaddr': for addr in self['ipaddr']: self.del_ip(*addr) for addr in data[key]: if isinstance(addr, basestring): addr = (addr, ) self.add_ip(*addr) elif key == 'ports': for port in self['ports']: self.del_port(port) for port in data[key]: self.add_port(port) elif key == 'vlans': for vlan in self['vlans']: self.del_vlan(vlan) for vlan in data[key]: if vlan != 1: self.add_vlan(vlan) elif key in ('neighbours', 'family'): # ignore on load pass else: self[key] = data[key] return self def load_dict(self, data): ''' Update the interface info from a dictionary. This call always bypasses open transactions, loading changes directly into the interface data. ''' with self._direct_state: self.load(data) 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: if self['ipdb_scope'] == 'locked': # do not touch locked interfaces return if self['ipdb_scope'] in ('shadow', 'create'): # ignore non-broadcast messages if dev['header']['sequence_number'] != 0: return # ignore ghost RTM_NEWLINK messages if (config.kernel[0] < 3) and \ (not dev.get_attr('IFLA_AF_SPEC')): return for (name, value) in dev.items(): self[name] = value for cell in dev['attrs']: # # Parse on demand # # At that moment, being not referenced, the # NLA is not decoded (yet). Calling # `__getitem__()` on nla_slot triggers the # NLA decoding, if the nla is referenced: # norm = ifinfmsg.nla2name(cell[0]) if norm not in self.cleanup: self[norm] = cell[1] # 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') self['vlan_protocol'] = data\ .get_attr('IFLA_VLAN_PROTOCOL') self['vlan_flags'] = data\ .get_attr('IFLA_VLAN_FLAGS', {})\ .get('flags', 0) if kind in ('vxlan', 'macvlan', 'macvtap', 'gre', 'gretap', 'ipvlan', 'bridge', 'ip6gre', 'ip6gretap'): data = linkinfo.get_attr('IFLA_INFO_DATA') or {} for nla in data.get('attrs', []): norm = ifinfmsg.nla2name(nla[0]) self[norm] = nla[1] # load vlans if dev['family'] == AF_BRIDGE: spec = dev.get_attr('IFLA_AF_SPEC') if spec is not None: vlans = spec.get_attrs('IFLA_BRIDGE_VLAN_INFO') vmap = {} for vlan in vlans: vmap[vlan['vid']] = vlan vids = set(vmap.keys()) # remove vids we do not have anymore for vid in (self['vlans'] - vids): self.del_vlan(vid) for vid in (vids - self['vlans']): self.add_vlan(vmap[vid]) protinfo = dev.get_attr('IFLA_PROTINFO') if protinfo is not None: for attr, value in protinfo['attrs']: attr = attr[5:].lower() self[attr] = value # 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['neighbours'] = self.ipdb.neighbours[self['index']] # finally, cleanup all not needed for item in self.cleanup: if item in self: del self[item] # AF_BRIDGE messages for bridges contain # IFLA_MASTER == self.index, we should fix it if self.get('master', None) == self['index']: self['master'] = None self['ipdb_scope'] = 'system' def wait_ip(self, *argv, **kwarg): return self['ipaddr'].wait_ip(*argv, **kwarg) @with_transaction def add_ip(self, ip, mask=None, broadcast=None, anycast=None, scope=None): ''' Add IP address to an interface Keyword arguments: * mask * broadcast * anycast * scope ''' # split mask if mask is None: ip, mask = ip.split('/') if mask.find('.') > -1: mask = dqn2int(mask) else: mask = int(mask, 0) elif isinstance(mask, basestring): mask = dqn2int(mask) # normalize the address if ip.find(':') > -1: ip = inet_ntop(AF_INET6, inet_pton(AF_INET6, ip)) # if it is a transaction or an interface update, apply the change self['ipaddr'].unlink((ip, mask)) request = {} if broadcast is not None: request['broadcast'] = broadcast if anycast is not None: request['anycast'] = anycast if scope is not None: request['scope'] = scope self['ipaddr'].add((ip, mask), raw=request) @with_transaction def del_ip(self, 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) # normalize the address if ip.find(':') > -1: ip = inet_ntop(AF_INET6, inet_pton(AF_INET6, ip)) if (ip, mask) in self['ipaddr']: self['ipaddr'].unlink((ip, mask)) self['ipaddr'].remove((ip, mask)) @with_transaction def add_vlan(self, vlan): if isinstance(vlan, dict): vid = vlan['vid'] else: vid = vlan vlan = {'vid': vlan, 'flags': 0} self['vlans'].unlink(vid) self['vlans'].add(vid, raw=vlan) @with_transaction def del_vlan(self, vlan): if vlan in self['vlans']: self['vlans'].unlink(vlan) self['vlans'].remove(vlan) @with_transaction def add_port(self, port): ''' Add port to a bridge or bonding ''' ifindex = self._resolve_port(port) if not ifindex: self._delay_add_port.add(port) else: self['ports'].unlink(ifindex) self['ports'].add(ifindex) @with_transaction def del_port(self, port): ''' Remove port from a bridge or bonding ''' ifindex = self._resolve_port(port) if not ifindex: self._delay_del_port.add(port) else: self['ports'].unlink(ifindex) self['ports'].remove(ifindex) 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 review(self): ret = super(Interface, self).review() last = self.current_tx if self['ipdb_scope'] == 'create': ret['+ipaddr'] = last['ipaddr'] ret['+ports'] = last['ports'] ret['+vlans'] = last['vlans'] del ret['ports'] del ret['ipaddr'] del ret['vlans'] if last._delay_add_port: ports = set(['*%s' % x for x in last._delay_add_port]) if '+ports' in ret: ret['+ports'] |= ports else: ret['+ports'] = ports if last._delay_del_port: ports = set(['*%s' % x for x in last._delay_del_port]) if '-ports' in ret: ret['-ports'] |= ports else: ret['-ports'] = ports return ret def _run(self, cmd, *argv, **kwarg): try: return cmd(*argv, **kwarg) except Exception as error: if self.partial: self.errors.append(error) return [] raise error def _resolve_port(self, port): # for now just a stupid resolver, will be # improved later with search by mac, etc. if isinstance(port, Interface): return port['index'] else: return self.ipdb.interfaces.get(port, {}).get('index', None) def commit(self, tid=None, transaction=None, commit_phase=1, commit_mask=0xff, newif=False): ''' Commit transaction. In the case of exception all changes applied during commit will be reverted. ''' if not commit_phase & commit_mask: return self error = None added = None removed = None drop = True init = None debug = {'traceback': None, 'transaction': None, 'next_stage': None} if tid: transaction = self.global_tx[tid] else: if transaction: drop = False else: transaction = self.current_tx if transaction.partial: transaction.errors = [] with self._write_lock: # if the interface does not exist, create it first ;) if self['ipdb_scope'] != 'system': # a special case: transition "create" -> "remove" if transaction['ipdb_scope'] == 'remove' and \ self['ipdb_scope'] == 'create': self.ipdb.interfaces._detach(self['ifname'], 0) return self newif = True self.set_target('ipdb_scope', 'system') try: # 8<--------------------------------------------------- # link resolve if self._deferred_link: link_key, link_obj = self._deferred_link transaction[link_key] = self._resolve_port(link_obj) self._deferred_link = None # 8<---------------------------------------------------- # ACHTUNG: hack for old platforms if self['address'] == '00:00:00:00:00:00': with self._direct_state: self['address'] = None self['broadcast'] = None # 8<---------------------------------------------------- init = self.pick() try: request = {key: transaction[key] for key in filter(lambda x: x[:3] != 'br_', transaction)} for key in ('net_ns_fd', 'net_ns_pid'): if key in request: with self._direct_state: self[key] = None del request[key] self.nl.link('add', **request) except NetlinkError as x: # File exists if x.code == errno.EEXIST: # A bit special case, could be one of two cases: # # 1. A race condition between two different IPDB # processes # 2. An attempt to create dummy0, gre0, bond0 when # the corrseponding module is not loaded. Being # loaded, the module creates a default interface # by itself, causing the request to fail # # The exception in that case can cause the DB # inconsistence, since there can be queued not only # the interface creation, but also IP address # changes etc. # # So we ignore this particular exception and try to # continue, as it is created by us. # # 3. An attempt to create VLAN or VXLAN interface # with the same ID but under different name # # In that case we should forward error properly if self['kind'] in ('vlan', 'vxlan'): newif = x else: raise except Exception as e: if transaction.partial: transaction.errors.append(e) raise PartialCommitException() else: # If link('add', ...) raises an exception, no netlink # broadcast will be sent, and the object is unmodified. # After the exception forwarding, the object is ready # to repeat the commit() call. raise if transaction['ipdb_scope'] == 'create' and commit_phase > 1: if self['index']: wd = self.ipdb.watchdog(action='RTM_DELLINK', ifname=self['ifname']) with self._direct_state: self['ipdb_scope'] = 'locked' self.nl.link('delete', index=self['index']) wd.wait() self.load_dict(transaction) return self elif newif: # Here we come only if a new interface is created # if commit_phase == 1 and not self.wait_target('ipdb_scope'): self.invalidate() if isinstance(newif, Exception): raise newif else: raise CreateException() # Re-populate transaction.ipaddr to have a proper IP target # # The reason behind the code is that a new interface in the # "up" state will have automatic IPv6 addresses, that aren't # reflected in the transaction. This may cause a false IP # target mismatch and a commit failure. # # To avoid that, collect automatic addresses to the # transaction manually, since it is not yet properly linked. # for addr in self.ipdb.ipaddr[self['index']]: transaction['ipaddr'].add(addr) # now we have our index and IP set and all other stuff snapshot = self.pick() # make snapshots of all dependent routes if commit_phase == 1 and hasattr(self.ipdb, 'routes'): self.routes = [] for record in self.ipdb.routes.filter({'oif': self['index']}): # For MPLS routes the key is an integer # They should match anyways if getattr(record['key'], 'table', None) != 255: self.routes.append((record['route'], record['route'].pick())) # resolve all delayed ports def resolve_ports(transaction, ports, callback, self, drop): def error(x): return KeyError('can not resolve port %s' % x) for port in tuple(ports): ifindex = self._resolve_port(port) if not ifindex: if transaction.partial: transaction.errors.append(error(port)) else: if drop: self.drop(transaction.uid) raise error(port) else: ports.remove(port) with transaction._direct_state: # ???? callback(ifindex) resolve_ports(transaction, transaction._delay_add_port, transaction.add_port, self, drop) resolve_ports(transaction, transaction._delay_del_port, transaction.del_port, self, drop) try: removed, added = snapshot // transaction run = transaction._run nl = transaction.nl # 8<--------------------------------------------- # Port vlans if removed['vlans'] or added['vlans']: if added['vlans']: transaction['vlans'].add(1) self['vlans'].set_target(transaction['vlans']) for i in removed['vlans']: if i != 1: # remove vlan from the port run(nl.vlan_filter, 'del', index=self['index'], vlan_info=self['vlans'][i]) for i in added['vlans']: if i != 1: # add vlan to the port run(nl.vlan_filter, 'add', index=self['index'], vlan_info=transaction['vlans'][i]) self['vlans'].target.wait(SYNC_TIMEOUT) if not self['vlans'].target.is_set(): raise CommitException('vlans target is not set') # 8<--------------------------------------------- # Ports if removed['ports'] or added['ports']: self['ports'].set_target(transaction['ports']) for i in removed['ports']: # detach port if i in self.ipdb.interfaces: (self.ipdb.interfaces[i] .set_target('master', None) .mirror_target('master', 'link')) run(nl.link, 'set', index=i, master=0) else: transaction.errors.append(KeyError(i)) for i in added['ports']: # attach port if i in self.ipdb.interfaces: (self.ipdb.interfaces[i] .set_target('master', self['index']) .mirror_target('master', 'link')) run(nl.link, 'set', index=i, master=self['index']) else: transaction.errors.append(KeyError(i)) self['ports'].target.wait(SYNC_TIMEOUT) if not self['ports'].target.is_set(): raise CommitException('ports target is not set') # 1. wait for proper targets on ports # 2. wait for mtu sync # # the bridge mtu is set from the port, if the latter is smaller # the bond mtu sets the port mtu, if the latter is smaller # # FIXME: team interfaces? for i in list(added['ports']) + list(removed['ports']): port = self.ipdb.interfaces[i] # port update target = port._local_targets['master'] target.wait(SYNC_TIMEOUT) with port._write_lock: 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']: if port.if_master != self['index']: raise CommitException('master set failed') else: if port.if_master == self['index']: raise CommitException('master unset failed') # master update if self['kind'] == 'bridge' and self['mtu'] > port['mtu']: self.set_target('mtu', port['mtu']) self.wait_target('mtu') # 8<--------------------------------------------- # Interface changes request = {} brequest = {} prequest = {} # preseed requests with the interface kind request['kind'] = self['kind'] brequest['kind'] = self['kind'] wait_all = False for key in added: if (key not in self._virtual_fields) and (key != 'kind'): if key[:3] == 'br_': brequest[key] = added[key] elif key[:7] == 'brport_': prequest[key[7:]] = added[key] else: request[key] = added[key] # FIXME: flush the interface type so the next two conditions # will work correctly request['kind'] = None brequest['kind'] = None # apply changes only if there is something to apply if (self['kind'] == 'bridge') and \ any([brequest[item] is not None for item in brequest]): brequest['index'] = self['index'] brequest['kind'] = self['kind'] brequest['family'] = AF_BRIDGE wait_all = True run(nl.link, (RTM_NEWLINK, NLM_F_REQUEST | NLM_F_ACK), **brequest) if any([request[item] is not None for item in request]): request['index'] = self['index'] request['kind'] = self['kind'] if request.get('address', None) == '00:00:00:00:00:00': request.pop('address') request.pop('broadcast', None) wait_all = True run(nl.link, 'set', **request) # Yet another trick: setting ifalias doesn't cause # netlink updates if 'ifalias' in request: self.reload() if any([prequest[item] is not None for item in prequest]): prequest['index'] = self['index'] run(nl.brport, "set", **prequest) if (wait_all) and (not transaction.partial): transaction.wait_all_targets() # 8<--------------------------------------------- # VLAN flags -- a dirty hack, pls do something with it if added.get('vlan_flags') is not None: run(nl.link, (RTM_NEWLINK, NLM_F_REQUEST | NLM_F_ACK), **{'kind': 'vlan', 'index': self['index'], 'vlan_flags': added['vlan_flags']}) # 8<--------------------------------------------- # IP address changes for _ in range(3): ip2add = transaction['ipaddr'] - self['ipaddr'] ip2remove = self['ipaddr'] - transaction['ipaddr'] if not ip2add and not ip2remove: break self['ipaddr'].set_target(transaction['ipaddr']) ### # Remove # # The promote_secondaries sysctl causes the kernel # to add secondary addresses back after the primary # address is removed. # # The library can not tell this from the result of # an external program. # # One simple way to work that around is to remove # secondaries first. rip = sorted(ip2remove, key=lambda x: self['ipaddr'][x]['flags'], reverse=True) # 8<-------------------------------------- for i in rip: # When you remove a primary IP addr, all the # subnetwork can be removed. In this case you # will fail, but it is OK, no need to roll back try: run(nl.addr, 'delete', self['index'], i[0], i[1]) except NetlinkError as x: # bypass only errno 99, # 'Cannot assign address' if x.code != errno.EADDRNOTAVAIL: raise except socket_error as x: # bypass illegal IP requests if isinstance(x.args[0], basestring) and \ x.args[0].startswith('illegal IP'): continue raise ### # Add addresses # 8<-------------------------------------- for i in ip2add: # Try to fetch additional address attributes try: kwarg = dict([k for k in transaction['ipaddr'][i].items() if k[0] in ('broadcast', 'anycast', 'scope')]) except KeyError: kwarg = None try: # feed the address to the OS run(nl.addr, 'add', self['index'], i[0], i[1], **kwarg if kwarg else {}) except NetlinkError as x: if x.code != errno.EEXIST: raise # 8<-------------------------------------- # some 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 # # FIXME: # # that all is a dirtiest hack ever, pls do # something with it # if (not self['flags'] & 1) or hasattr(self.ipdb.nl, 'netns'): # 1. flush old IPv6 addresses for addr in list(self['ipaddr'].ipv6): self['ipaddr'].remove(addr) # 2. reload addresses for addr in self.nl.get_addr(index=self['index'], family=AF_INET6): self.ipdb.ipaddr._new(addr) # if there are tons of IPv6 addresses, it may take a # really long time, and that's bad, but it's broken in # the kernel :| # 8<-------------------------------------- self['ipaddr'].target.wait(SYNC_TIMEOUT) if self['ipaddr'].target.is_set(): break else: raise CommitException('ipaddr target is not set') # 8<--------------------------------------------- # Iterate callback chain for ch in self._commit_hooks: # An exception will rollback the transaction ch(self.dump(), snapshot.dump(), transaction.dump()) # 8<--------------------------------------------- # Move the interface to a netns if ('net_ns_fd' in added) or ('net_ns_pid' in added): request = {} for key in ('net_ns_fd', 'net_ns_pid'): if key in added: request[key] = added[key] request['index'] = self['index'] run(nl.link, 'set', **request) countdown = 10 while countdown: # wait until the interface will disappear # from the current network namespace -- # up to 1 second (make it configurable?) try: self.nl.get_links(self['index']) except NetlinkError as e: if e.code == errno.ENODEV: break raise except Exception: raise countdown -= 1 time.sleep(0.1) # 8<--------------------------------------------- # Interface removal if added.get('ipdb_scope') in ('shadow', 'remove'): wd = self.ipdb.watchdog(action='RTM_DELLINK', ifname=self['ifname']) with self._direct_state: self['ipdb_scope'] = 'locked' self.nl.link('delete', index=self['index']) wd.wait() with self._direct_state: self['ipdb_scope'] = 'shadow' # system-wide checks if commit_phase == 1: self.ipdb.ensure('run') if added.get('ipdb_scope') == 'remove': self.ipdb.interfaces._detach(None, self['index'], None) if drop: self.drop(transaction.uid) return self # 8<--------------------------------------------- # system-wide checks if commit_phase == 1: self.ipdb.ensure('run') except Exception as e: error = e # log the error environment debug['traceback'] = traceback.format_exc() debug['transaction'] = transaction debug['next_stage'] = None # something went wrong: roll the transaction back if commit_phase == 1: if newif: drop = False try: self.commit(transaction=init if newif else snapshot, commit_phase=2, commit_mask=commit_mask, newif=newif) except Exception as i_e: debug['next_stage'] = i_e error = RuntimeError() else: # 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 links = self.nl.get_links() for link in links: self.ipdb.interfaces._new(link) links = self.nl.get_vlans() for link in links: self.ipdb.interfaces._new(link) for addr in self.nl.get_addr(): self.ipdb.ipaddr._new(addr) for key in ('ipaddr', 'ports', 'vlans'): self[key].clear_target() # raise partial commit exceptions if transaction.partial and transaction.errors: error = PartialCommitException('partial commit error') # if it is not a rollback turn if drop and commit_phase == 1: # drop last transaction in any case self.drop(transaction.uid) # raise exception for failed transaction if error is not None: error.debug = debug raise error # restore dependent routes for successful rollback if commit_phase == 2: for route in self.routes: with route[0]._direct_state: route[0]['ipdb_scope'] = 'restore' try: route[0].commit(transaction=route[1], commit_phase=2, commit_mask=2) except RuntimeError as x: # RuntimeError is raised due to phase 2, so # an additional check is required if isinstance(x.cause, NetlinkError) and \ x.cause.code == errno.EEXIST: pass time.sleep(config.commit_barrier) # drop all collected errors, if any self.errors = [] return self def up(self): ''' Shortcut: change the interface state to 'up'. ''' if self['flags'] is None: self['flags'] = 1 else: if not self['flags'] & 1: self['flags'] |= 1 elif self.current_tx is None: self.begin() return self def down(self): ''' Shortcut: change the interface state to 'down'. ''' if self['flags'] is None: self['flags'] = 0 else: if self['flags'] & 1: self['flags'] &= ~(self['flags'] & 1) elif self.current_tx is None: self.begin() return self def remove(self): ''' Mark the interface for removal ''' self['ipdb_scope'] = 'remove' 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['ipdb_scope'] = 'shadow' return self class InterfacesDict(Dotkeys): def __init__(self, ipdb): self.ipdb = ipdb self._event_map = {'RTM_NEWLINK': self._new, 'RTM_DELLINK': self._del} def _register(self): links = self.ipdb.nl.get_links() # iterate twice to map port/master relations for link in links: self._new(link, skip_master=True) for link in links: self._new(link) # load bridge vlan information links = self.ipdb.nl.get_vlans() for link in links: self._new(link) def add(self, kind, ifname, reuse=False, **kwarg): ''' Create new network interface ''' with self.ipdb.exclusive: # check for existing interface if ifname in self: if (self[ifname]['ipdb_scope'] == 'shadow') or reuse: device = self[ifname] kwarg['kind'] = kind device.load_dict(kwarg) if self[ifname]['ipdb_scope'] == 'shadow': with device._direct_state: device['ipdb_scope'] = 'create' device.begin() else: raise CreateException("interface %s exists" % ifname) else: device = self[ifname] = Interface(ipdb=self.ipdb, mode='snapshot') # delay link resolve? for key in kwarg: # any /.+link$/ attr if key[-4:] == 'link': if isinstance(kwarg[key], Interface): kwarg[key] = kwarg[key].get('index') or \ kwarg[key].get('ifname') if not isinstance(kwarg[key], int): device._deferred_link = (key, kwarg[key]) device._mode = self.ipdb.mode with device._direct_state: device['kind'] = kind device['index'] = kwarg.get('index', 0) device['ifname'] = ifname device['ipdb_scope'] = 'create' # set some specific attrs for attr in ('peer', 'uid', 'gid', 'ifr', 'mode', 'bond_mode', 'vrf_table', 'address'): if attr in kwarg: device[attr] = kwarg.pop(attr) device.begin() device.load(kwarg) return device def _del(self, msg): target = self.get(msg['index']) if target is None: return if msg['family'] == AF_BRIDGE: with target._direct_state: for vlan in tuple(target['vlans']): target.del_vlan(vlan) # check for freezed devices if getattr(target, '_freeze', None): with target._direct_state: target['ipdb_scope'] = 'shadow' return # check for locked devices if target.get('ipdb_scope') in ('locked', 'shadow'): return self._detach(None, msg['index'], msg) def _new(self, msg, skip_master=False): # check, if a record exists index = msg.get('index', None) ifname = msg.get_attr('IFLA_IFNAME', None) device = None cleanup = 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) and (ifname not in self): # scenario #1, new interface device = \ self[index] = \ self[ifname] = Interface(ipdb=self.ipdb) elif (index not in self) and (ifname in self): # scenario #2, index change old_index = self[ifname]['index'] device = self[index] = self[ifname] if old_index in self: cleanup = old_index if old_index in self.ipdb.ipaddr: self.ipdb.ipaddr[index] = \ self.ipdb.ipaddr[old_index] del self.ipdb.ipaddr[old_index] if old_index in self.ipdb.neighbours: self.ipdb.neighbours[index] = \ self.ipdb.neighbours[old_index] del self.ipdb.neighbours[old_index] else: # scenario #3, interface rename # scenario #4, assume rename old_name = self[index]['ifname'] if old_name != ifname: # unlink old name cleanup = old_name device = self[ifname] = self[index] if index not in self.ipdb.ipaddr: self.ipdb.ipaddr[index] = self.ipdb._ipaddr_set() if index not in self.ipdb.neighbours: self.ipdb.neighbours[index] = LinkedSet() # update port references old_master = device.get('master', None) new_master = msg.get_attr('IFLA_MASTER') if old_master != new_master: if old_master in self: with self[old_master]._direct_state: if index in self[old_master]['ports']: self[old_master].del_port(index) if new_master in self and new_master != index: with self[new_master]._direct_state: self[new_master].add_port(index) if cleanup is not None: del self[cleanup] if skip_master: msg.strip('IFLA_MASTER') device.load_netlink(msg) if new_master is None: with device._direct_state: device['master'] = None def _detach(self, name, idx, msg=None): with self.ipdb.exclusive: if msg is not None: if msg['event'] == 'RTM_DELLINK' and \ msg['change'] != 0xffffffff: return if idx is None or idx < 1: target = self[name] idx = target['index'] else: target = self[idx] name = target['ifname'] # clean up port, if exists master = target.get('master', None) if master in self and target['index'] in self[master]['ports']: with self[master]._direct_state: self[master].del_port(target) self.pop(name, None) self.pop(idx, None) self.ipdb.ipaddr.pop(idx, None) self.ipdb.neighbours.pop(idx, None) with target._direct_state: target['ipdb_scope'] = 'detached' class AddressesDict(dict): def __init__(self, ipdb): self.ipdb = ipdb self._event_map = {'RTM_NEWADDR': self._new, 'RTM_DELADDR': self._del} def _register(self): for msg in self.ipdb.nl.get_addr(): self._new(msg) def reload(self): # Reload addresses from the kernel. # (This is a workaround to reorder primary and secondary addresses.) for k in self.keys(): self[k] = self.ipdb._ipaddr_set() for msg in self.ipdb.nl.get_addr(): self._new(msg) for idx in self.keys(): iff = self.ipdb.interfaces[idx] with iff._direct_state: iff['ipaddr'] = self[idx] def _new(self, msg): if msg['family'] == AF_INET: addr = msg.get_attr('IFA_LOCAL') elif msg['family'] == AF_INET6: addr = msg.get_attr('IFA_ADDRESS') else: return raw = {'local': msg.get_attr('IFA_LOCAL'), 'broadcast': msg.get_attr('IFA_BROADCAST'), 'address': msg.get_attr('IFA_ADDRESS'), 'flags': msg.get_attr('IFA_FLAGS') or msg.get('flags'), 'prefixlen': msg['prefixlen'], 'family': msg['family']} try: self[msg['index']].add(key=(addr, raw['prefixlen']), raw=raw) except: pass def _del(self, msg): if msg['family'] == AF_INET: addr = msg.get_attr('IFA_LOCAL') elif msg['family'] == AF_INET6: addr = msg.get_attr('IFA_ADDRESS') else: return try: self[msg['index']].remove((addr, msg['prefixlen'])) except: pass class NeighboursDict(dict): def __init__(self, ipdb): self.ipdb = ipdb self._event_map = {'RTM_NEWNEIGH': self._new, 'RTM_DELNEIGH': self._del} def _register(self): for msg in self.ipdb.nl.get_neighbours(): self._new(msg) def _new(self, msg): if msg['family'] == AF_BRIDGE: return try: (self[msg['ifindex']] .add(key=msg.get_attr('NDA_DST'), raw={'lladdr': msg.get_attr('NDA_LLADDR')})) except: pass def _del(self, msg): if msg['family'] == AF_BRIDGE: return try: (self[msg['ifindex']] .remove(msg.get_attr('NDA_DST'))) except: pass spec = [{'name': 'interfaces', 'class': InterfacesDict, 'kwarg': {}}, {'name': 'by_name', 'class': View, 'kwarg': {'path': 'interfaces', 'constraint': lambda k, v: isinstance(k, basestring)}}, {'name': 'by_index', 'class': View, 'kwarg': {'path': 'interfaces', 'constraint': lambda k, v: isinstance(k, int)}}, {'name': 'ipaddr', 'class': AddressesDict, 'kwarg': {}}, {'name': 'neighbours', 'class': NeighboursDict, 'kwarg': {}}] pyroute2-0.4.21/pyroute2/ipdb/linkedset.py0000664000175000017500000002236413127143031020352 0ustar peetpeet00000000000000''' ''' import struct import threading from collections import OrderedDict from socket import inet_pton from socket import AF_INET from socket import AF_INET6 from pyroute2.common import basestring 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) def _check_default_target(self): if self._ct is not None: if set(filter(self.target_filter, self)) == \ set(filter(self.target_filter, self._ct)): self._ct = None return True return False self.lock = threading.RLock() self.target = threading.Event() self.targets = {self.target: _check_default_target} self._ct = None self.raw = OrderedDict() self.links = [] self.exclusive = set() def __getitem__(self, key): return self.raw[key] def clear_target(self, target=None): with self.lock: if target is None: self._ct = None self.target.clear() else: target.clear() del self.targets[target] def set_target(self, value, ignore_state=False): ''' 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 isinstance(value, (set, tuple, list)): self._ct = set(value) self.target.clear() # immediately check, if the target already # reached -- otherwise you will miss the # target forever if not ignore_state: self.check_target() elif hasattr(value, '__call__'): new_target = threading.Event() self.targets[new_target] = value if not ignore_state: self.check_target() return new_target else: raise TypeError("target type not supported") 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: for evt in self.targets: if self.targets[evt](self): evt.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 super(LinkedSet, self).add(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 super(LinkedSet, self).remove(key) self.raw.pop(key, None) 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. ''' if not isinstance(link, LinkedSet): raise TypeError() self.links.append(link) def disconnect(self, link): self.links.remove(link) def __repr__(self): return repr(tuple(self)) class IPaddrSet(LinkedSet): ''' LinkedSet child class with different target filter. The filter ignores link local IPv6 addresses when sets and checks the target. The `wait_ip()` routine by default does not ignore link local IPv6 addresses, but it may be changed with the `ignore_link_local` argument. ''' @property def ipv4(self): ret = IPaddrSet() for x in self: if self[x]['family'] == AF_INET: ret.add(x, self[x]) return ret @property def ipv6(self): ret = IPaddrSet() for x in self: if self[x]['family'] == AF_INET6: ret.add(x, self[x]) return ret def wait_ip(self, net, mask=None, timeout=None, ignore_link_local=False): family = AF_INET6 if net.find(':') >= 0 else AF_INET alen = 32 if family == AF_INET else 128 net = inet_pton(family, net) if mask is None: mask = alen if family == AF_INET: net = struct.unpack('>I', net)[0] else: na, nb = struct.unpack('>QQ', net) net = (na << 64) | nb match = net & (((1 << mask) - 1) << (alen - mask)) def match_ip(ipset): for rnet, rmask in ipset: rfamily = AF_INET6 if rnet.find(':') >= 0 else AF_INET if family != rfamily: continue if family == AF_INET6 and \ ignore_link_local and \ rnet[:4] == 'fe80' and \ rmask == 64: continue rnet = inet_pton(family, rnet) if family == AF_INET: rnet = struct.unpack('>I', rnet)[0] else: rna, rnb = struct.unpack('>QQ', rnet) rnet = (rna << 64) | rnb if (rnet & (((1 << mask) - 1) << (alen - mask))) == match: return True return False target = self.set_target(match_ip) target.wait(timeout) ret = target.is_set() self.clear_target(target) return ret def __getitem__(self, key): if isinstance(key, (tuple, list)): return self.raw[key] elif isinstance(key, int): return self.raw[tuple(self.raw.keys())[key]] elif isinstance(key, basestring): key = key.split('/') key = (key[0], int(key[1])) return self.raw[key] else: TypeError('wrong key type') class SortedIPaddrSet(IPaddrSet): def __init__(self, *argv, **kwarg): super(SortedIPaddrSet, self).__init__(*argv, **kwarg) if argv and isinstance(argv[0], SortedIPaddrSet): # Re-initialize self.raw from argv[0].raw to preserve order: self.raw = OrderedDict(argv[0].raw) def __and__(self, other): nset = SortedIPaddrSet(self) return nset.__iand__(other) def __iand__(self, other): for key in self.raw: if key not in other: self.remove(key) return self def __rand__(self, other): return self.__and__(other) def __xor__(self, other): nset = SortedIPaddrSet(self) return nset.__ixor__(other) def __ixor__(self, other): if not isinstance(other, SortedIPaddrSet): return RuntimeError('SortedIPaddrSet instance required') xor_keys = set(self.raw.keys()) ^ set(other.raw.keys()) for key in xor_keys: if key in self: self.remove(key) else: self.add(key, raw=other.raw[key], cascade=False) return self def __rxor__(self, other): return self.__xor__(other) def __or__(self, other): nset = SortedIPaddrSet(self) return nset.__ior__(other) def __ior__(self, other): if not isinstance(other, SortedIPaddrSet): return RuntimeError('SortedIPaddrSet instance required') for key, value in other.raw.items(): if key not in self: self.add(key, raw=value, cascade=False) return self def __ror__(self, other): return self.__or__(other) def __sub__(self, other): nset = SortedIPaddrSet(self) return nset.__isub__(other) def __isub__(self, other): for key in other: if key in self: self.remove(key) return self def __iter__(self): return iter(self.raw) pyroute2-0.4.21/pyroute2/ipdb/transactional.py0000664000175000017500000004025713152305337021242 0ustar peetpeet00000000000000''' ''' import logging import threading from pyroute2.common import uuid32 from pyroute2.common import Dotkeys from pyroute2.ipdb.linkedset import LinkedSet from pyroute2.ipdb.exceptions import CommitException # How long should we wait on EACH commit() checkpoint: for ipaddr, # ports etc. That's not total commit() timeout. SYNC_TIMEOUT = 5 log = logging.getLogger(__name__) 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): if self.flag < 1: raise RuntimeError('release unlocked state') 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): if self._mode == 'snapshot': # short-circuit with self._write_lock: return f(self, True, *argv, **kwarg) elif self._mode == 'readonly': raise RuntimeError('can not change readonly object') with self._write_lock: direct = self._direct_state.is_set() if not direct: # 1. 'implicit': begin transaction, if there is none if self._mode == 'implicit': if not self.current_tx: self.begin() # 2. require open transaction for 'explicit' type elif self._mode == 'explicit': if not self.current_tx: raise TypeError('start a transaction first') # do not support other modes else: raise TypeError('transaction mode not supported') # now that the transaction _is_ open return f(self, direct, *argv, **kwarg) decorated.__doc__ = f.__doc__ return decorated def with_transaction(f): def decorated(self, direct, *argv, **kwarg): if direct: f(self, *argv, **kwarg) else: transaction = self.current_tx f(transaction, *argv, **kwarg) return self return update(decorated) class Transactional(Dotkeys): ''' Utility class that implements common transactional logic. ''' _fields = [] _virtual_fields = [] _fields_cmp = {} _linked_sets = [] _nested = [] 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 uuid32() self.last_error = None self._commit_hooks = [] self._sids = [] self._ts = threading.local() self._snapshots = {} self.global_tx = {} self._targets = {} self._local_targets = {} self._write_lock = threading.RLock() self._direct_state = State(self._write_lock) self._linked_sets = self._linked_sets or set() # for i in self._fields: Dotkeys.__setitem__(self, i, None) @property def ro(self): return self.pick(detached=False, readonly=True) def register_commit_hook(self, hook): ''' ''' self._commit_hooks.append(hook) def unregister_commit_hook(self, hook): ''' ''' with self._write_lock: for cb in tuple(self._commit_hooks): if hook == cb: self._commit_hooks.pop(self._commit_hooks.index(cb)) ## # Object serialization: dump, pick 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 pick(self, detached=True, uid=None, parent=None, readonly=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 self[key] is not None: if key in self._fields: res[key] = self[key] for key in self._linked_sets: res[key] = type(self[key])(self[key]) if not detached: self[key].connect(res[key]) if readonly: res._mode = 'readonly' return res ## # Context management: enter, exit def __enter__(self): if self._mode == 'readonly': return self elif self._mode not in ('implicit', 'explicit'): raise TypeError('context managers require a transactional mode') if not self.current_tx: self.begin() return self def __exit__(self, exc_type, exc_value, traceback): # apply transaction only if there was no error if self._mode == 'readonly': return elif exc_type is None: try: self.commit() except Exception as e: self.last_error = e raise ## # Implicit object transfomations def __repr__(self): res = {} for i in tuple(self): if self[i] is not None: res[i] = self[i] return res.__repr__() ## # Object ops: +, -, /, ... def __sub__(self, vs): # create result res = {} with self._direct_state: # simple keys for key in self: if (key in self._fields): if ((key not in vs) or (self[key] != vs[key])): res[key] = self[key] for key in self._linked_sets: diff = type(self[key])(self[key] - vs[key]) if diff: res[key] = diff else: res[key] = set() for key in self._nested: res[key] = self[key] - vs[key] return res def __floordiv__(self, vs): left = {} right = {} with self._direct_state: with vs._direct_state: for key in set(tuple(self.keys()) + tuple(vs.keys())): if self.get(key, None) != vs.get(key, None): left[key] = self.get(key) right[key] = vs.get(key) continue if key not in self: right[key] = vs[key] elif key not in vs: left[key] = self[key] for key in self._linked_sets: ldiff = type(self[key])(self[key] - vs[key]) rdiff = type(vs[key])(vs[key] - self[key]) if ldiff: left[key] = ldiff else: left[key] = set() if rdiff: right[key] = rdiff else: right[key] = set() for key in self._nested: left[key], right[key] = self[key] // vs[key] return left, right ## # Methods to be overloaded def detach(self): pass def load(self, data): pass def commit(self, *args, **kwarg): pass def last_snapshot_id(self): return self._sids[-1] def invalidate(self): # on failure, invalidate the interface and detach it # from the parent # 0. obtain lock on IPDB, to avoid deadlocks # ... all the DB updates will wait with self.ipdb.exclusive: # 1. drop the IPRoute() link self.nl = None # 2. clean up ipdb self.detach() # 3. invalidate the interface with self._direct_state: for i in tuple(self.keys()): del self[i] self['ipdb_scope'] = 'invalid' # 4. the rest self._mode = 'invalid' ## # Snapshot methods def revert(self, sid): with self._write_lock: assert sid in self._snapshots self.local_tx[sid] = self._snapshots[sid] self.global_tx[sid] = self._snapshots[sid] self.current_tx = self._snapshots[sid] self._sids.remove(sid) del self._snapshots[sid] return self def snapshot(self, sid=None): ''' Create new snapshot ''' if self._parent: raise RuntimeError("Can't init snapshot from a nested object") if (self.ipdb is not None) and self.ipdb._stop: raise RuntimeError("Can't create snapshots on released IPDB") t = self.pick(detached=True, uid=sid) self._snapshots[t.uid] = t self._sids.append(t.uid) for key, value in t.items(): if isinstance(value, Transactional): value.snapshot(sid=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]] ## # Current tx def _set_current_tx(self, tx): with self._write_lock: self._ts.current = tx def _get_current_tx(self): ''' The current active transaction (thread-local) ''' with self._write_lock: if not hasattr(self._ts, 'current'): self._ts.current = None return self._ts.current current_tx = property(_get_current_tx, _set_current_tx) ## # Local tx registry def _get_local_tx(self): with self._write_lock: if not hasattr(self._ts, 'tx'): self._ts.tx = {} return self._ts.tx local_tx = property(_get_local_tx) ## # Transaction ops: begin, review, drop def begin(self): ''' Start new transaction ''' if self._parent is not None: self._parent.begin() else: return self._begin() def _begin(self, tid=None): if (self.ipdb is not None) and self.ipdb._stop: raise RuntimeError("Can't start transaction on released IPDB") t = self.pick(detached=False, uid=tid) self.local_tx[t.uid] = t self.global_tx[t.uid] = t if self.current_tx is None: self.current_tx = t for key, value in t.items(): if isinstance(value, Transactional): # start transaction on a nested object value._begin(tid=t.uid) # link transaction to own one t[key] = value.global_tx[t.uid] return t.uid def review(self, tid=None): ''' Review the changes made in the transaction `tid` or in the current active transaction (thread-local) ''' if self.current_tx is None: raise TypeError('start a transaction first') tid = tid or self.current_tx.uid if self.get('ipdb_scope') == 'create': if self.current_tx is not None: prime = self.current_tx else: log.warning('the "create" scope without transaction') prime = self return dict([(x[0], x[1]) for x in prime.items() if x[1] is not None]) with self._write_lock: added = self.global_tx[tid] - self removed = self - self.global_tx[tid] 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. If tid is not specified, drop the current one. ''' with self._write_lock: if tid is None: tx = self.current_tx if tx is None: raise TypeError("no transaction") else: tx = self.global_tx[tid] if self.current_tx == tx: self.current_tx = None # detach linked sets for key in self._linked_sets: if tx[key] in self[key].links: self[key].disconnect(tx[key]) for (key, value) in self.items(): if isinstance(value, Transactional): try: value.drop(tx.uid) except KeyError: pass # finally -- delete the transaction del self.local_tx[tx.uid] del self.global_tx[tx.uid] ## # Property ops: set/get/delete @update def __setitem__(self, direct, key, value): if not direct: # automatically set target on the active transaction, # which must be started prior to that call transaction = self.current_tx transaction[key] = value if value is not None: transaction._targets[key] = threading.Event() else: # set the item Dotkeys.__setitem__(self, key, value) # update on local targets with self._write_lock: 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.global_tx.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): # firstly set targets self[key] = None # then continue with delete if not direct: transaction = self.current_tx 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 wait_target(self, key, timeout=SYNC_TIMEOUT): self._local_targets[key].wait(SYNC_TIMEOUT) with self._write_lock: return self._local_targets.pop(key).is_set() def set_target(self, key, value): with self._write_lock: self._local_targets[key] = threading.Event() self._local_targets[key].value = value if self.get(key) == value: self._local_targets[key].set() return self def mirror_target(self, key_from, key_to): with self._write_lock: self._local_targets[key_to] = self._local_targets[key_from] return self def set(self, key, value): self[key] = value return self pyroute2-0.4.21/pyroute2/ipdb/main.py0000664000175000017500000012703213152331050017310 0ustar peetpeet00000000000000# -*- coding: utf-8 -*- ''' IPDB module =========== Basically, IPDB is a transactional database, containing records, that represent network stack objects. Any change in the database is not reflected immediately in OS, 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, just forwards requests to the kernel, and doesn't wait for the system state. So it's up to developer to check, whether the requested object is really set up or not. 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. But unlike IPRoute, the IPDB ensures the changes to be reflected in the system:: with IPDB() as ipdb: with ipdb.interfaces['eth0'] as i: i.up() i.add_ip('192.168.0.2/24') i.add_ip('192.168.0.3/24') # ---> <--- here you can expect `eth0` is up # and has these two addresses, so # the following code can rely on that So IPDB is updated asynchronously, but the `commit()` operation is synchronous. At least, it is planned to be such. NB: *In the example above `commit()` is implied with the `__exit__()` of the `with` statement.* The choice between IPDB and IPRoute 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 the 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. IPDB and other software ----------------------- IPDB is designed to be a non-exclusive network settings database. There may be several IPDB instances on the same OS, as well as other network management software, such as NetworkManager etc. The IPDB transactions should not interfere with other software settings, unless they touch the same objects. E.g., if IPDB brings an interface up, while NM shuts it down, there will be a race condition. An example:: # IPDB code # NetworkManager at the same time: ipdb.interfaces['eth0'].up() # ipdb.interfaces['eth0'].commit() # $ sudo nmcli con down eth0 # ---> <--- # The eth0 state here is undefined. Some of the commands # above will fail But as long as the software doesn't touch the same objects, there will be no conflicts. Another example:: # IPDB code # At the same time, NetworkManager with ipdb.interfaces['eth0'] as i: # adds addresses: i.add_ip('172.16.254.2/24') # * 10.0.0.2/24 i.add_ip('172.16.254.3/24') # * 10.0.0.3/24 # ---> <--- # At this point the eth0 interface will have all four addresses. # If the IPDB transaction fails by some reason, only IPDB addresses # will be rolled back. There may be a need to prevent other software from changing the network settings. There is no locking at the kernel level, but IPDB can revert all the changes as soon as they appear on the interface:: # IPDB code ipdb.interfaces['eth0'].freeze() # Here some other software tries to # add an address, or to remove the old # one # ---> <--- # At this point the eth0 interface will have all the same settings as # at the `freeze()` call moment. Newly added addresses will be removed, # all the deleted addresses will be restored. # # Please notice, that an address removal may cause also a routes removal, # and that is the thing that IPDB can not neither prevent, nor revert. ipdb.interfaces['eth0'].unfreeze() Quickstart ---------- Simple tutorial:: from pyroute2 import IPDB # several IPDB instances are supported within on process ipdb = IPDB() # commit is called automatically upon the exit from `with` # statement with ipdb.interfaces.eth0 as i: i.address = '00:11:22:33:44:55' i.ifname = 'bala' i.txqlen = 2000 # basic routing support ipdb.routes.add({'dst': 'default', 'gateway': '10.0.0.1'}).commit() # do not forget to shutdown IPDB ipdb.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 supports functional-like syntax also:: from pyroute2 import IPDB with IPDB() as ipdb: intf = (ipdb.interfaces['eth0'] .add_ip('10.0.0.2/24') .add_ip('10.0.0.3/24') .set_address('00:11:22:33:44:55') .set_mtu(1460) .set_name('external') .commit()) # ---> <--- here you have the interface reference with # all the changes applied: renamed, added ipaddr, # changed macaddr and mtu. ... # some code # pls notice, that the interface reference will not work # outside of `with IPDB() ...` Transaction modes ----------------- IPDB has several operating modes: - '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 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 `current_tx` transaction, and `commit()` or `drop()` it. Also, multiple 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:: ipdb.interfaces['tap0'].down() ipdb.interfaces['tap0']['address'] = '00:11:22:33:44:55' ... Context managers ---------------- Transactional objects (interfaces, routes) can act as context managers in the same way as IPDB does itself:: with ipdb.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. Read-only interface views ------------------------- Using an interface as a context manager **will** start a transaction. Sometimes it is not what one needs. To avoid unnecessary transactions, and to avoid the risk to occasionally change interface attributes, one can use read-only views:: with ipdb.interfaces[1].ro as iface: print(iface.ifname) print(iface.address) The `.ro` view neither starts transactions, nor allows to change anything, raising the `RuntimeError` exception. The same read-only views are available for routes and rules. Create interfaces ----------------- IPDB can also create virtual interfaces:: with ipdb.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 `IPDB.create()` call has the same syntax as `IPRoute.link('add', ...)`, except you shouldn't specify the `'add'` command. Refer to `IPRoute` docs for details. Please notice, that the interface object stays in the database even if there was an error during the interface creation. It is done so to make it possible to fix the interface object and try to run `commit()` again. Or you can drop the interface object with the `.remove().commit()` call. IP address management --------------------- IP addresses on interfaces may be managed using `add_ip()` and `del_ip()`:: with ipdb.interfaces['eth0'] as eth: eth.add_ip('10.0.0.1/24') eth.add_ip('10.0.0.2/24') eth.add_ip('2001:4c8:1023:108::39/64') eth.del_ip('172.16.12.5/24') The address format may be either a string with `'address/mask'` notation, or a pair of `'address', mask`:: with ipdb.interfaces['eth0'] as eth: eth.add_ip('10.0.0.1', 24) eth.del_ip('172.16.12.5', 24) The `ipaddr` attribute contains all the IP addresses of the interface, which are acessible in different ways. Getting an iterator from `ipaddr` gives you a sequence of tuples `('address', mask)`:: >>> for addr in ipdb.interfaces['eth0'].ipaddr: ... print(ipaddr) ... ('10.0.0.2', 24) ('10.0.0.1', 24) Getting one IP from `ipaddr` returns a dict object with full spec: >>> ipdb.interfaces['eth0'].ipaddr[0]: {'family': 2, 'broadcast': None, 'flags': 128, 'address': '10.0.0.2', 'prefixlen': 24, 'local': '10.0.0.2'} >>> ipdb.intefaces['eth0'].ipaddr['10.0.0.2/24']: {'family': 2, 'broadcast': None, 'flags': 128, 'address': '10.0.0.2', 'prefixlen': 24, 'local': '10.0.0.2'} The API is a bit weird, but it's because of historical reasons. In the future it may be changed. Another feature of the `ipaddr` attribute is views:: >>> ipdb.interfaces['eth0'].ipaddr.ipv4: (('10.0.0.2', 24), ('10.0.0.1', 24)) >>> ipdb.interfaces['eth0'].ipaddr.ipv6: (('2001:4c8:1023:108::39', 64),) The views, as well as the `ipaddr` attribute itself are not supposed to be changed by user, but only by the internal API. Bridge interfaces ----------------- Modern kernels provide possibility to manage bridge interface properties such as STP, forward delay, ageing time etc. Names of these properties start with `br_`, like `br_ageing_time`, `br_forward_delay` e.g.:: [x for x in dir(ipdb.interfaces.virbr0) if x.startswith('br_')] Bridge ports ------------ IPDB supports specific bridge port parameters, such as proxyarp, unicast/multicast flood, cost etc.:: with ipdb.interfaces['br-port0'] as p: p.brport_cost = 200 p.brport_unicast_flood = 0 p.brport_proxyarp = 0 Ports management ---------------- IPDB provides a uniform API to manage bridge, bond and vrf ports:: with ipdb.interfaces['br-int'] as br: br.add_port('veth0') br.add_port(ipdb.interfaces.veth1) br.add_port(700) br.del_port('veth2') Both `add_port()` and `del_port()` accept three types of arguments: * `'veth0'` -- interface name as a string * `ipdb.interfaces.veth1` -- IPDB interface object * `700` -- interface index, an integer Routes management ----------------- IPDB has a simple yet useful routing management interface. Create a route ~~~~~~~~~~~~~~ To add a route, there is an easy to use 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 ipdb.routes.add(spec).commit() # pass spec as kwargs ipdb.routes.add(**spec).commit() # use keyword arguments explicitly ipdb.routes.add(dst='172.16.1.0/24', oif=4, ...).commit() Please notice, that the device can be specified with `oif` (output interface) or `iif` (input interface), the `device` keyword is not supported anymore. More examples:: # specify table and priority (ipdb.routes .add(dst='172.16.1.0/24', gateway='192.168.0.1', table=100, priority=10) .commit()) The `priority` field is what the `iproute2` utility calls `metric` -- see also below. Get a route ~~~~~~~~~~~ To access and change the routes, one can use notations as follows:: # default table (254) # # change the route gateway and mtu # with ipdb.routes['172.16.1.0/24'] as route: route.gateway = '192.168.122.60' route.metrics.mtu = 1500 # access the default route print(ipdb.routes['default']) # change the default gateway with ipdb.routes['default'] as route: route.gateway = '10.0.0.1' By default, the path `ipdb.routes` reflects only the main routing table (254). But Linux supports much more routing tables, so does IPDB:: In [1]: ipdb.routes.tables.keys() Out[1]: [0, 254, 255] In [2]: len(ipdb.routes.tables[255]) Out[2]: 11 # => 11 automatic routes in the table local It is important to understand, that routing tables keys in IPDB are not only the destination prefix. The key consists of 'prefix/mask' string and the route priority (if any):: In [1]: ipdb.routes.tables[254].idx.keys() Out[1]: [RouteKey(dst='default', scope=0, table=254, family=2, ...), RouteKey(dst='172.17.0.0/16', scope=253, table=254, ...), RouteKey(dst='172.16.254.0/24', scope=253, table=254, ...), RouteKey(dst='192.168.122.0/24', scope=253, table=254, ...), RouteKey(dst='fe80::/64', scope=0, table=254, family=10, ...)] But a routing table in IPDB allows several variants of the route spec. The simplest case is to retrieve a route by prefix, if there is only one match:: # get route by prefix ipdb.routes['172.16.1.0/24'] # get route by a special name ipdb.routes['default'] If there are more than one route that matches the spec, only the first one will be retrieved. One should iterate all the records and filter by a key to retrieve all matches:: # only one route will be retrieved ipdb.routes['fe80::/64'] # get all routes by this prefix [ x for x in ipdb.routes if x['dst'] == 'fe80::/64' ] It is also possible to use dicts as specs:: # get IPv4 default route ipdb.routes[{'dst': 'default', 'family': AF_INET}] # get IPv6 default route ipdb.routes[{'dst': 'default', 'family': AF_INET6}] # get route by priority ipdb.routes.table[100][{'dst': '10.0.0.0/24', 'priority': 10}] While this notation returns one route, there is a method to get all the routes matching the spec:: # get all the routes from all the tables via some interface ipdb.routes.filter({'oif': idx}) # get all IPv6 routes from some table ipdb.routes.table[tnum].filter({'family': AF_INET6}) Route metrics ~~~~~~~~~~~~~ A special object is dedicated to route metrics, one can access it via `route.metrics` or `route['metrics']`:: # these two statements are equal: with ipdb.routes['172.16.1.0/24'] as route: route['metrics']['mtu'] = 1400 with ipdb.routes['172.16.1.0/24'] as route: route.metrics.mtu = 1400 Possible metrics are defined in `rtmsg.py:rtmsg.metrics`, e.g. `RTAX_HOPLIMIT` means `hoplimit` metric etc. Multipath routing ~~~~~~~~~~~~~~~~~ Multipath nexthops are managed via `route.add_nh()` and `route.del_nh()` methods. They are available to review via `route.multipath`, but one should not directly add/remove/modify nexthops in `route.multipath`, as the changes will not be committed correctly. To create a multipath route:: ipdb.routes.add({'dst': '172.16.232.0/24', 'multipath': [{'gateway': '172.16.231.2', 'hops': 2}, {'gateway': '172.16.231.3', 'hops': 1}, {'gateway': '172.16.231.4'}]}).commit() To change a multipath route:: with ipdb.routes['172.16.232.0/24'] as r: r.add_nh({'gateway': '172.16.231.5'}) r.del_nh({'gateway': '172.16.231.4'}) Another possible way is to create a normal route and turn it into multipath by `add_nh()`:: # create a non-MP route with one gateway: (ipdb .routes .add({'dst': '172.16.232.0/24', 'gateway': '172.16.231.2'}) .commit()) # turn it to become a MP route: (ipdb .routes['172.16.232.0/24'] .add_nh({'gateway': '172.16.231.3'}) .commit()) # here the route will contain two NH records, with # gateways 172.16.231.2 and 172.16.231.3 # remove one NH and turn the route to be a normal one (ipdb .routes['172.16.232.0/24'] .del_nh({'gateway': '172.16.231.2'}) .commit()) # thereafter the traffic to 172.16.232.0/24 will go only # via 172.16.231.3 Differences from the iproute2 syntax ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ By historical reasons, `iproute2` uses names that differs from what the kernel uses. E.g., `iproute2` uses `weight` for multipath route hops instead of `hops`, where `weight == (hops + 1)`. Thus, a route created with `hops == 2` will be listed by `iproute2` as `weight 3`. Another significant difference is `metrics`. The `pyroute2` library uses the kernel naming scheme, where `metrics` means mtu, rtt, window etc. The `iproute2` utility uses `metric` (not `metrics`) as a name for the `priority` field. In examples:: # ------------------------------------------------------- # iproute2 command: $ ip route add default \\ nexthop via 172.16.0.1 weight 2 \\ nexthop via 172.16.0.2 weight 9 # pyroute2 code: (ipdb .routes .add({'dst': 'default', 'multipath': [{'gateway': '172.16.0.1', 'hops': 1}, {'gateway': '172.16.0.2', 'hops': 8}]) .commit()) # ------------------------------------------------------- # iproute2 command: $ ip route add default via 172.16.0.2 metric 200 # pyroute2 code: (ipdb .routes .add({'dst': 'default', 'gateway': '172.16.0.2', 'priority': 200}) .commit()) # ------------------------------------------------------- # iproute2 command: $ ip route add default via 172.16.0.2 mtu 1460 # pyroute2 code: (ipdb .routes .add({'dst': 'default', 'gateway': '172.16.0.2', 'metrics': {'mtu': 1460}}) .commit()) Multipath default routes ~~~~~~~~~~~~~~~~~~~~~~~~ .. warning:: As of the merge of kill_rtcache into the kernel, and it's release in ~3.6, weighted default routes no longer work in Linux. Please refer to https://github.com/svinota/pyroute2/issues/171#issuecomment-149297244 for details. Rules management ---------------- IPDB provides a basic IP rules management system. Create a rule ~~~~~~~~~~~~~ Syntax is almost the same as for routes:: # rule spec spec = {'src': '172.16.1.0/24', 'table': 200, 'priority': 15000} ipdb.rules.add(spec).commit() Get a rule ~~~~~~~~~~ The way IPDB handles IP rules is almost the same as routes, but rule keys are more complicated -- the Linux kernel doesn't use keys for rules, but instead iterates all the records until the first one w/o any attribute mismatch. The fields that the kernel uses to compare rules, IPDB uses as the key fields (see `pyroute2/ipdb/rule.py:RuleKey`) There are also more ways to find a record, as with routes:: # 1. iterate all the records for record in ipdb.rules: match(record) # 2. an integer as the key matches the first # rule with that priority ipdb.rules[32565] # 3. a dict as the key returns the first match # for all the specified attrs ipdb.rules[{'dst': '10.0.0.0/24', 'table': 200}] Priorities ~~~~~~~~~~ Thus, the rule priority is **not** a key, neither in the kernel, nor in IPDB. One should **not** rely on priorities as on keys, there may be several rules with the same priority, and it often happens, e.g. on Android systems. Persistence ~~~~~~~~~~~ There is no *change* operation for the rule records in the kernel, so only *add/del* work. When IPDB changes a record, it effectively deletes the old one and creates the new with new parameters, but the object, referring the record, stays the same. Also that means, that IPDB can not recognize the situation, when someone else does the same. So if there is another program changing records by *del/add* operations, even another IPDB instance, referring objects in the IPDB will be recreated. 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. The class API ------------- ''' import sys import atexit import logging import traceback import threading from functools import partial from pprint import pprint from pyroute2 import config from pyroute2.common import uuid32 from pyroute2.common import basestring from pyroute2.iproute import IPRoute from pyroute2.netlink.rtnl import RTM_GETLINK, RTNL_GROUPS from pyroute2.netlink.rtnl.ifinfmsg import ifinfmsg from pyroute2.ipdb import rules from pyroute2.ipdb import routes from pyroute2.ipdb import interfaces from pyroute2.ipdb.routes import BaseRoute from pyroute2.ipdb.transactional import SYNC_TIMEOUT from pyroute2.ipdb.linkedset import IPaddrSet from pyroute2.ipdb.linkedset import SortedIPaddrSet from pyroute2.ipdb.utils import test_reachable_icmp log = logging.getLogger(__name__) class Watchdog(object): def __init__(self, ipdb, action, kwarg): self.event = threading.Event() self.is_set = False 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.is_set = True self.event.set() self.cb = cb # register callback prior to other things self.uuid = self.ipdb.register_callback(self.cb) def wait(self, timeout=SYNC_TIMEOUT): ret = self.event.wait(timeout=timeout) self.cancel() return ret def cancel(self): self.ipdb.unregister_callback(self.uuid) 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, nl_async=None, nl_bind_groups=RTNL_GROUPS, ignore_rtables=None, callbacks=None, sort_addresses=False, plugins=None): plugins = plugins or ['interfaces', 'routes', 'rules'] pmap = {'interfaces': interfaces, 'routes': routes, 'rules': rules} self.mode = mode self._stdout = sys.stdout self._ipaddr_set = SortedIPaddrSet if sort_addresses else IPaddrSet self._event_map = {} self._deferred = {} self._ensure = [] self._loaded = set() self._mthread = None self._nl_own = nl is None self._nl_async = config.ipdb_nl_async if nl_async is None else True self.mnl = None self.nl = nl self.nl_bind_groups = nl_bind_groups self._plugins = [pmap[x] for x in plugins if x in pmap] if isinstance(ignore_rtables, int): self._ignore_rtables = [ignore_rtables, ] elif isinstance(ignore_rtables, (list, tuple, set)): self._ignore_rtables = ignore_rtables else: self._ignore_rtables = [] self._stop = False # see also 'register_callback' self._post_callbacks = {} self._pre_callbacks = {} self._cb_threads = {} # locks and events self.exclusive = threading.RLock() self._shutdown_lock = threading.Lock() # register callbacks # # examples:: # def cb1(ipdb, msg, event): # print(event, msg) # def cb2(...): # ... # # # default mode: post # IPDB(callbacks=[cb1, cb2]) # # specify the mode explicitly # IPDB(callbacks=[(cb1, 'pre'), (cb2, 'post')]) # for cba in callbacks or []: if not isinstance(cba, (tuple, list, set)): cba = (cba, ) self.register_callback(*cba) # load information self.restart_on_error = restart_on_error if \ restart_on_error is not None else nl is None # init the database self.initdb() # init the dir() cache self.__dir_cache__ = [i for i in self.__class__.__dict__.keys() if i[0] != '_'] self.__dir_cache__.remove('serve_forever') self.__dir_cache__.extend(list(self._deferred.keys())) # atexit.register(self.release) def __dir__(self): return self.__dir_cache__ def __enter__(self): return self def __exit__(self, exc_type, exc_value, traceback): self.release() def initdb(self): # common event map, empty by default, so all the # events aer just ignored self.release(complete=False) self._stop = False # explicitly cleanup object references for event in tuple(self._event_map): del self._event_map[event] # if the command socket is not provided, create it if self._nl_own: self.nl = IPRoute() # setup monitoring socket self.mnl = self.nl.clone() try: self.mnl.bind(groups=self.nl_bind_groups, async=self._nl_async) except: self.mnl.close() if self._nl_own is None: self.nl.close() raise # explicitly cleanup references for key in tuple(self._deferred): del self._deferred[key] for module in self._plugins: if (module.groups & self.nl_bind_groups) != module.groups: continue for plugin in module.spec: self._deferred[plugin['name']] = module.spec if plugin['name'] in self._loaded: delattr(self, plugin['name']) self._loaded.remove(plugin['name']) # start the monitoring thread self._mthread = threading.Thread(name="IPDB event loop", target=self.serve_forever) self._mthread.setDaemon(True) self._mthread.start() def __getattribute__(self, name): deferred = super(IPDB, self).__getattribute__('_deferred') if name in deferred: register = [] spec = deferred[name] for plugin in spec: obj = plugin['class'](self, **plugin['kwarg']) setattr(self, plugin['name'], obj) register.append(obj) self._loaded.add(plugin['name']) del deferred[plugin['name']] for obj in register: if hasattr(obj, '_register'): obj._register() if hasattr(obj, '_event_map'): for event in obj._event_map: if event not in self._event_map: self._event_map[event] = [] self._event_map[event].append(obj._event_map[event]) return super(IPDB, self).__getattribute__(name) 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: - callback function - mode (optional, default="post") The callback should be a routine, that accepts three arguments:: cb(ipdb, msg, action) Arguments are: - **ipdb** is a reference to IPDB instance, that invokes the callback. - **msg** is a message arrived - **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 safe.lock = lock safe.uuid = uuid32() if mode == 'post': self._post_callbacks[safe.uuid] = safe elif mode == 'pre': self._pre_callbacks[safe.uuid] = safe else: raise KeyError('Unknown callback mode') return safe.uuid def unregister_callback(self, cuid, mode='post'): if mode == 'post': cbchain = self._post_callbacks elif mode == 'pre': cbchain = self._pre_callbacks else: raise KeyError('Unknown callback mode') safe = cbchain[cuid] with safe.lock: cbchain.pop(cuid) for t in tuple(self._cb_threads.get(cuid, ())): t.join(3) ret = self._cb_threads.get(cuid, ()) return ret def release(self, complete=True): ''' 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 if self.mnl is not None: # terminate the main loop for t in range(3): try: msg = ifinfmsg() msg['index'] = 1 msg.reset() self.mnl.put(msg, RTM_GETLINK) except Exception as e: log.warning("shutdown error: %s", e) # Just give up. # We can not handle this case if self._mthread is not None: self._mthread.join() if self.mnl is not None: self.mnl.close() self.mnl = None if complete or self._nl_own: self.nl.close() self.nl = None with self.exclusive: # collect all the callbacks for cuid in tuple(self._cb_threads): for t in tuple(self._cb_threads[cuid]): t.join() # flush all the objects def flush(idx): for key in tuple(idx.keys()): try: del idx[key] except KeyError: pass idx_list = [] if 'interfaces' in self._loaded: for (key, dev) in self.by_name.items(): try: # FIXME self.interfaces._detach(key, dev['index'], dev.nlmsg) except KeyError: pass idx_list.append(self.ipaddr) idx_list.append(self.neighbours) if 'routes' in self._loaded: idx_list.extend([self.routes.tables[x] for x in self.routes.tables.keys()]) if 'rules' in self._loaded: idx_list.append(self.rules) for idx in idx_list: flush(idx) def create(self, kind, ifname, reuse=False, **kwarg): return self.interfaces.add(kind, ifname, reuse, **kwarg) def ensure(self, cmd='add', reachable=None, condition=None): if cmd == 'reset': self._ensure = [] elif cmd == 'run': for f in self._ensure: f() elif cmd == 'add': if isinstance(reachable, basestring): reachable = reachable.split(':') if len(reachable) == 1: f = partial(test_reachable_icmp, reachable[0]) else: raise NotImplementedError() self._ensure.append(f) else: if sys.stdin.isatty(): pprint(self._ensure, stream=self._stdout) elif cmd == 'print': pprint(self._ensure, stream=self._stdout) elif cmd == 'get': return self._ensure else: raise NotImplementedError() def items(self): # TODO: add support for filters? # iterate interfaces for ifname in getattr(self, 'by_name', {}): yield (('interfaces', ifname), self.interfaces[ifname]) # iterate routes for table in getattr(getattr(self, 'routes', None), 'tables', {}): for key, route in self.routes.tables[table].items(): yield (('routes', table, key), route) def dump(self): ret = {} for key, obj in self.items(): ptr = ret for step in key[:-1]: if step not in ptr: ptr[step] = {} ptr = ptr[step] ptr[key[-1]] = obj return ret def load(self, config, ptr=None): if ptr is None: ptr = self for key in config: obj = getattr(ptr, key, None) if obj is not None: if hasattr(obj, 'load'): obj.load(config[key]) else: self.load(config[key], ptr=obj) elif hasattr(ptr, 'add'): ptr.add(**config[key]) return self def review(self): ret = {} for key, obj in self.items(): ptr = ret try: rev = obj.review() except TypeError: continue for step in key[:-1]: if step not in ptr: ptr[step] = {} ptr = ptr[step] ptr[key[-1]] = rev if not ret: raise TypeError('no transaction started') return ret def drop(self): ok = False for key, obj in self.items(): try: obj.drop() except TypeError: continue ok = True if not ok: raise TypeError('no transaction started') def commit(self, transactions=None, phase=1): # what to commit: either from transactions argument, or from # started transactions on existing objects if transactions is None: # collect interface transactions txlist = [(x, x.current_tx) for x in getattr(self, 'by_name', {}).values() if x.local_tx.values()] # collect route transactions for table in getattr(getattr(self, 'routes', None), 'tables', {}).keys(): txlist.extend([(x, x.current_tx) for x in self.routes.tables[table] if x.local_tx.values()]) transactions = txlist snapshots = [] removed = [] tx_ipdb_prio = [] tx_main = [] tx_prio1 = [] tx_prio2 = [] tx_prio3 = [] for (target, tx) in transactions: # 8<------------------------------ # first -- explicit priorities if tx['ipdb_priority']: tx_ipdb_prio.append((target, tx)) continue # 8<------------------------------ # routes if isinstance(target, BaseRoute): tx_prio3.append((target, tx)) continue # 8<------------------------------ # intefaces kind = target.get('kind', None) if kind in ('vlan', 'vxlan', 'gre', 'tuntap', 'vti', 'vrf'): tx_prio1.append((target, tx)) elif kind in ('bridge', 'bond'): tx_prio2.append((target, tx)) else: tx_main.append((target, tx)) # 8<------------------------------ # explicitly sorted transactions tx_ipdb_prio = sorted(tx_ipdb_prio, key=lambda x: x[1]['ipdb_priority'], reverse=True) # FIXME: this should be documented # # The final transactions order: # 1. any txs with ipdb_priority (sorted by that field) # # Then come default priorities (no ipdb_priority specified): # 2. all the rest # 3. vlan, vxlan, gre, tuntap, vti, vrf # 4. bridge, bond # 5. routes transactions = tx_ipdb_prio + tx_main + tx_prio1 + tx_prio2 + tx_prio3 try: for (target, tx) in transactions: if target['ipdb_scope'] == 'detached': continue if tx['ipdb_scope'] == 'remove': tx['ipdb_scope'] = 'shadow' removed.append((target, tx)) if phase == 1: s = (target, target.pick(detached=True)) snapshots.append(s) # apply the changes, but NO rollback -- only phase 1 target.commit(transaction=tx, commit_phase=phase, commit_mask=phase) # if the commit above fails, the next code # branch will run rollbacks except Exception: if phase == 1: # run rollbacks for ALL the collected transactions, # even successful ones self.fallen = transactions txs = filter(lambda x: not ('create' == x[0]['ipdb_scope'] == x[1]['ipdb_scope']), snapshots) self.commit(transactions=txs, phase=2) raise else: if phase == 1: for (target, tx) in removed: target['ipdb_scope'] = 'detached' target.detach() finally: if phase == 1: for (target, tx) in transactions: target.drop(tx.uid) return self def watchdog(self, action='RTM_NEWLINK', **kwarg): return Watchdog(self, action, kwarg) def serve_forever(self): ### # Main monitoring cycle. It gets messages from the # default iproute queue and updates objects in the # database. # # Should not be called manually. ### while not self._stop: try: messages = self.mnl.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: log.error('Restarting IPDB instance after ' 'error:\n%s', traceback.format_exc()) if self.restart_on_error: try: self.initdb() except: log.error('Error restarting DB:\n%s', traceback.format_exc()) return continue else: raise RuntimeError('Emergency shutdown') for msg in messages: # Run pre-callbacks # NOTE: pre-callbacks are synchronous for (cuid, cb) in tuple(self._pre_callbacks.items()): try: cb(self, msg, msg['event']) except: pass with self.exclusive: event = msg.get('event', None) if event in self._event_map: for func in self._event_map[event]: func(msg) # run post-callbacks # NOTE: post-callbacks are asynchronous for (cuid, cb) in tuple(self._post_callbacks.items()): t = threading.Thread(name="IPDB callback %s" % (id(cb)), target=cb, args=(self, msg, msg['event'])) t.start() if cuid not in self._cb_threads: self._cb_threads[cuid] = set() self._cb_threads[cuid].add(t) # occasionally join cb threads for cuid in tuple(self._cb_threads): for t in tuple(self._cb_threads.get(cuid, ())): t.join(0) if not t.is_alive(): try: self._cb_threads[cuid].remove(t) except KeyError: pass if len(self._cb_threads.get(cuid, ())) == 0: del self._cb_threads[cuid] pyroute2-0.4.21/pyroute2/ipdb/__init__.py0000664000175000017500000000000013123443516020115 0ustar peetpeet00000000000000pyroute2-0.4.21/pyroute2/ipdb/exceptions.py0000664000175000017500000000030013123443516020542 0ustar peetpeet00000000000000 class DeprecationException(Exception): pass class CommitException(Exception): pass class CreateException(Exception): pass class PartialCommitException(Exception): pass pyroute2-0.4.21/pyroute2/ipdb/rules.py0000664000175000017500000002322413152331050017514 0ustar peetpeet00000000000000import logging import threading from socket import AF_INET from socket import AF_INET6 from collections import namedtuple from pyroute2.netlink import rtnl from pyroute2.netlink.rtnl.fibmsg import fibmsg from pyroute2.netlink.rtnl.fibmsg import FR_ACT_NAMES from pyroute2.ipdb.exceptions import CommitException from pyroute2.ipdb.transactional import Transactional log = logging.getLogger(__name__) groups = rtnl.RTNLGRP_IPV4_RULE |\ rtnl.RTNLGRP_IPV6_RULE RuleKey = namedtuple('RuleKey', ('action', 'table', 'priority', 'iifname', 'oifname', 'fwmark', 'fwmask', 'family', 'goto', 'tun_id')) class Rule(Transactional): ''' Persistent transactional rule object ''' _fields = [fibmsg.nla2name(i[1]) for i in fibmsg.nla_map] for key, _ in fibmsg.fields: _fields.append(key) _fields.append('removal') _virtual_fields = ['ipdb_scope', 'ipdb_priority'] _fields.extend(_virtual_fields) cleanup = ('attrs', 'header', 'event', 'src_len', 'dst_len', 'res1', 'res2') @classmethod def make_key(cls, msg): values = [] if isinstance(msg, fibmsg): for field in RuleKey._fields: v = msg.get_attr(msg.name2nla(field)) if v is None: v = msg.get(field, 0) values.append(v) elif isinstance(msg, dict): for field in RuleKey._fields: values.append(msg.get(field, 0)) else: raise TypeError('prime not supported: %s' % type(msg)) return RuleKey(*values) def __init__(self, ipdb, mode=None, parent=None, uid=None): Transactional.__init__(self, ipdb, mode, parent, uid) with self._direct_state: self['ipdb_priority'] = 0 def load_netlink(self, msg): with self._direct_state: if self['ipdb_scope'] == 'locked': # do not touch locked interfaces return self['ipdb_scope'] = 'system' for (key, value) in msg.items(): self[key] = value # merge NLA for cell in msg['attrs']: # # Parse on demand # norm = fibmsg.nla2name(cell[0]) if norm in self.cleanup: continue self[norm] = cell[1] if msg.get_attr('FRA_DST'): dst = '%s/%s' % (msg.get_attr('FRA_DST'), msg['dst_len']) self['dst'] = dst if msg.get_attr('FRA_SRC'): src = '%s/%s' % (msg.get_attr('FRA_SRC'), msg['src_len']) self['src'] = src # finally, cleanup all not needed for item in self.cleanup: if item in self: del self[item] return self def commit(self, tid=None, transaction=None, commit_phase=1, commit_mask=0xff): if not commit_phase & commit_mask: return self error = None drop = True devop = 'set' if tid: transaction = self.global_tx[tid] else: if transaction: drop = False else: transaction = self.current_tx # create a new route if self['ipdb_scope'] != 'system': devop = 'add' # work on an existing route snapshot = self.pick() added, removed = transaction // snapshot added.pop('ipdb_scope', None) removed.pop('ipdb_scope', None) try: # rule add/set if any(added.values()) or devop == 'add': old_key = self.make_key(self) new_key = self.make_key(transaction) if new_key != old_key: # check for the key conflict if new_key in self.ipdb.rules: raise CommitException('rule priority conflict') else: self.ipdb.rules[new_key] = self self.nl.rule('del', priority=old_key) self.nl.rule('add', **transaction) else: if devop != 'add': with self._direct_state: self['ipdb_scope'] = 'shadow' self.nl.rule('del', priority=old_key) with self._direct_state: self['ipdb_scope'] = 'reload' self.nl.rule('add', **transaction) transaction.wait_all_targets() # rule removal if (transaction['ipdb_scope'] in ('shadow', 'remove')) or\ ((transaction['ipdb_scope'] == 'create') and commit_phase == 2): if transaction['ipdb_scope'] == 'shadow': with self._direct_state: self['ipdb_scope'] = 'locked' # create watchdog wd = self.ipdb.watchdog('RTM_DELRULE', priority=self['priority']) for rule in self.nl.rule('delete', **snapshot): self.ipdb.rules.load_netlink(rule) wd.wait() if transaction['ipdb_scope'] == 'shadow': with self._direct_state: self['ipdb_scope'] = 'shadow' except Exception as e: if devop == 'add': error = e self.nl = None self['ipdb_scope'] = 'invalid' del self.ipdb.rules[self.make_key(self)] elif commit_phase == 1: ret = self.commit(transaction=snapshot, commit_phase=2, commit_mask=commit_mask) if isinstance(ret, Exception): error = ret else: error = e else: if drop: self.drop(transaction.uid) x = RuntimeError() x.cause = e raise x if drop and commit_phase == 1: self.drop(transaction.uid) if error is not None: error.transaction = transaction raise error return self def remove(self): self['ipdb_scope'] = 'remove' return self def shadow(self): self['ipdb_scope'] = 'shadow' return self class RulesDict(dict): def __init__(self, ipdb): self.ipdb = ipdb self.lock = threading.Lock() self._event_map = {'RTM_NEWRULE': self.load_netlink, 'RTM_DELRULE': self.load_netlink} def _register(self): for msg in self.ipdb.nl.get_rules(family=AF_INET): self.load_netlink(msg) for msg in self.ipdb.nl.get_rules(family=AF_INET6): self.load_netlink(msg) def __getitem__(self, key): with self.lock: if isinstance(key, RuleKey): return super(RulesDict, self).__getitem__(key) elif isinstance(key, tuple): return super(RulesDict, self).__getitem__(RuleKey(*key)) elif isinstance(key, int): for k in self.keys(): if key == k[2]: return super(RulesDict, self).__getitem__(k) elif isinstance(key, dict): for v in self.values(): for k in key: if key[k] != v.get(k, None): break else: return v def add(self, spec=None, **kwarg): ''' Create a rule from a dictionary ''' spec = dict(spec or kwarg) # action and priority are parts of the key, so # they must be specified if 'priority' not in spec: spec['priority'] = 32000 if 'table' in spec: spec['action'] = FR_ACT_NAMES['FR_ACT_TO_TBL'] elif 'goto' in spec: spec['action'] = FR_ACT_NAMES['FR_ACT_GOTO'] if 'family' not in spec: spec['family'] = AF_INET rule = Rule(self.ipdb) rule.update(spec) # setup the scope with rule._direct_state: rule['ipdb_scope'] = 'create' # rule.begin() for (key, value) in spec.items(): rule[key] = value self[rule.make_key(spec)] = rule return rule def load_netlink(self, msg): if not isinstance(msg, fibmsg): return key = Rule.make_key(msg) # RTM_DELRULE if msg['event'] == 'RTM_DELRULE': try: # locate the record record = self[key] # delete the record if record['ipdb_scope'] not in ('locked', 'shadow'): del self[key] with record._direct_state: record['ipdb_scope'] = 'detached' except Exception as e: # just ignore this failure for now log.debug("delrule failed for %s", e) return # RTM_NEWRULE if key not in self: self[key] = Rule(self.ipdb) self[key].load_netlink(msg) return self[key] spec = [{'name': 'rules', 'class': RulesDict, 'kwarg': {}}] pyroute2-0.4.21/pyroute2/ipdb/routes.py0000664000175000017500000012477413152331050017717 0ustar peetpeet00000000000000import time import types import struct import logging import threading from collections import namedtuple from socket import AF_UNSPEC from socket import AF_INET6 from socket import AF_INET from socket import inet_pton from socket import inet_ntop from pyroute2.common import AF_MPLS from pyroute2.common import basestring from pyroute2.netlink import rtnl from pyroute2.netlink import nlmsg from pyroute2.netlink import nlmsg_base from pyroute2.netlink.rtnl import rt_type from pyroute2.netlink.rtnl import rt_proto from pyroute2.netlink.rtnl import encap_type from pyroute2.netlink.rtnl.rtmsg import rtmsg from pyroute2.netlink.rtnl.req import IPRouteRequest from pyroute2.netlink.rtnl.ifaddrmsg import IFA_F_SECONDARY from pyroute2.ipdb.exceptions import CommitException from pyroute2.ipdb.transactional import Transactional from pyroute2.ipdb.transactional import with_transaction from pyroute2.ipdb.transactional import SYNC_TIMEOUT from pyroute2.ipdb.linkedset import LinkedSet log = logging.getLogger(__name__) groups = rtnl.RTNLGRP_IPV4_ROUTE |\ rtnl.RTNLGRP_IPV6_ROUTE |\ rtnl.RTNLGRP_MPLS_ROUTE IP6_RT_PRIO_USER = 1024 class Metrics(Transactional): _fields = [rtmsg.metrics.nla2name(i[0]) for i in rtmsg.metrics.nla_map] class Encap(Transactional): _fields = ['type', 'labels'] class Via(Transactional): _fields = ['family', 'addr'] class NextHopSet(LinkedSet): def __init__(self, prime=None): super(NextHopSet, self).__init__() prime = prime or [] for v in prime: self.add(v) def __sub__(self, vs): ret = type(self)() sub = set(self.raw.keys()) - set(vs.raw.keys()) for v in sub: ret.add(self[v], raw=self.raw[v]) return ret def __make_nh(self, prime): if isinstance(prime, BaseRoute): return prime.make_nh_key(prime) elif isinstance(prime, dict): if prime.get('family', None) == AF_MPLS: return MPLSRoute.make_nh_key(prime) else: return Route.make_nh_key(prime) elif isinstance(prime, tuple): return prime else: raise TypeError("unknown prime type %s" % type(prime)) def __getitem__(self, key): return self.raw[key] def __iter__(self): def NHIterator(): for x in tuple(self.raw.values()): yield x return NHIterator() def add(self, prime, raw=None, cascade=False): key = self.__make_nh(prime) r = key._required l = key._fields skey = key[:r] + (None, ) * (len(l) - r) if skey in self.raw: del self.raw[skey] return super(NextHopSet, self).add(key, raw=prime) def remove(self, prime, raw=None, cascade=False): key = self.__make_nh(prime) try: super(NextHopSet, self).remove(key) except KeyError as e: r = key._required l = key._fields skey = key[:r] + (None, ) * (len(l) - r) for rkey in tuple(self.raw.keys()): if skey == rkey[:r] + (None, ) * (len(l) - r): break else: raise e super(NextHopSet, self).remove(rkey) class WatchdogMPLSKey(dict): def __init__(self, route): dict.__init__(self) self['oif'] = route['oif'] self['dst'] = [{'ttl': 0, 'bos': 1, 'tc': 0, 'label': route['dst']}] class WatchdogKey(dict): ''' Construct from a route a dictionary that could be used as a match for IPDB watchdogs. ''' def __init__(self, route): dict.__init__(self, [x for x in IPRouteRequest(route).items() if x[0] in ('dst', 'dst_len', 'src', 'src_len', 'oif', 'iif', 'gateway', 'table') and x[1]]) # Universal route key RouteKey = namedtuple('RouteKey', ('dst', 'table', 'family', 'priority', 'tos', 'oif')) RouteKey._required = 5 # number of required fields (should go first) # IP multipath NH key IPNHKey = namedtuple('IPNHKey', ('gateway', 'encap', 'oif')) IPNHKey._required = 2 # MPLS multipath NH key MPLSNHKey = namedtuple('MPLSNHKey', ('newdst', 'via', 'oif')) MPLSNHKey._required = 2 def _normalize_ipaddr(x, y): if isinstance(y, basestring) and y.find(':') > -1: y = inet_ntop(AF_INET6, inet_pton(AF_INET6, y)) return x == y def _normalize_ipnet(x, y): # # x -- incoming value # y -- transaction value # if isinstance(y, basestring) and y.find(':') > -1: s = y.split('/') ip = inet_ntop(AF_INET6, inet_pton(AF_INET6, s[0])) if len(s) > 1: y = '%s/%s' % (ip, s[1]) else: y = ip return x == y class BaseRoute(Transactional): ''' Persistent transactional route object ''' _fields = [rtmsg.nla2name(i[0]) for i in rtmsg.nla_map] for key, _ in rtmsg.fields: _fields.append(key) _fields.append('removal') _virtual_fields = ['ipdb_scope', 'ipdb_priority'] _fields.extend(_virtual_fields) _linked_sets = ['multipath', ] _nested = [] _gctime = None cleanup = ('attrs', 'header', 'event', 'cacheinfo') _fields_cmp = {'src': _normalize_ipnet, 'dst': _normalize_ipnet, 'gateway': _normalize_ipaddr, 'prefsrc': _normalize_ipaddr} def __init__(self, ipdb, mode=None, parent=None, uid=None): Transactional.__init__(self, ipdb, mode, parent, uid) with self._direct_state: self['ipdb_priority'] = 0 @with_transaction def add_nh(self, prime): with self._write_lock: # if the multipath chain is empty, copy the current # nexthop as the first in the multipath if not self['multipath']: first = {} for key in [x for x in self._fields if x not in ('family', 'dst_len', 'src_len', 'tos', 'table', 'proto', 'scope', 'type', 'flags', 'dst', 'src', 'ipdb_scope', 'metrics', 'encap', 'via', 'multipath')]: if self[key]: first[key] = self[key] if first: if self['family']: first['family'] = self['family'] for key in ('encap', 'via', 'metrics'): if self[key] and any(self[key].values()): first[key] = self[key] self[key] = None self['multipath'].add(first) # cleanup key fields for key in ('oif', 'iif', 'gateway', 'newdst'): self[key] = None # add the prime as NH if self['family'] == AF_MPLS: prime['family'] = AF_MPLS self['multipath'].add(prime) @with_transaction def del_nh(self, prime): with self._write_lock: if not self['multipath']: raise KeyError('attempt to delete nexthop from ' 'non-multipath route') nh = dict(prime) if self['family'] == AF_MPLS: nh['family'] = AF_MPLS self['multipath'].remove(nh) def load_netlink(self, msg): with self._direct_state: if self['ipdb_scope'] == 'locked': # do not touch locked interfaces return self['ipdb_scope'] = 'system' for (key, value) in msg.items(): self[key] = value # cleanup multipath NH for nh in self['multipath']: self.del_nh(nh) # merge NLA for cell in msg['attrs']: # # Parse on demand # norm = rtmsg.nla2name(cell[0]) if norm in self.cleanup: continue value = cell[1] # normalize RTAX if norm == 'metrics': with self['metrics']._direct_state: for metric in tuple(self['metrics'].keys()): del self['metrics'][metric] for (rtax, rtax_value) in value['attrs']: rtax_norm = rtmsg.metrics.nla2name(rtax) self['metrics'][rtax_norm] = rtax_value elif norm == 'multipath': for record in value: nh = type(self)(ipdb=self.ipdb, parent=self) nh.load_netlink(record) with nh._direct_state: del nh['dst'] del nh['ipdb_scope'] del nh['ipdb_priority'] del nh['multipath'] del nh['metrics'] self['multipath'].add(nh) elif norm == 'encap': with self['encap']._direct_state: ret = [] for l in value.get_attr('MPLS_IPTUNNEL_DST'): ret.append(str(l['label'])) self['encap']['labels'] = '/'.join(ret) elif norm == 'via': with self['via']._direct_state: self['via'] = value elif norm == 'newdst': self['newdst'] = [x['label'] for x in value] else: self[norm] = value if msg.get('family', 0) == AF_MPLS: dst = msg.get_attr('RTA_DST') if dst: dst = dst[0]['label'] else: if msg.get_attr('RTA_DST'): dst = '%s/%s' % (msg.get_attr('RTA_DST'), msg['dst_len']) else: dst = 'default' self['dst'] = dst # fix RTA_ENCAP_TYPE if needed if msg.get_attr('RTA_ENCAP'): if self['encap_type'] is not None: with self['encap']._direct_state: self['encap']['type'] = self['encap_type'] self['encap_type'] = None # or drop encap, if there is no RTA_ENCAP in msg elif self['encap'] is not None: self['encap_type'] = None with self['encap']._direct_state: self['encap'] = {} # drop metrics, if there is no RTA_METRICS in msg if not msg.get_attr('RTA_METRICS') and self['metrics'] is not None: with self['metrics']._direct_state: self['metrics'] = {} # same for via if not msg.get_attr('RTA_VIA') and self['via'] is not None: with self['via']._direct_state: self['via'] = {} # one hop -> multihop transition if not msg.get_attr('RTA_GATEWAY') and self['gateway'] is not None: self['gateway'] = None if 'oif' not in msg and \ not msg.get_attr('RTA_OIF') and \ self['oif'] is not None: self['oif'] = None # finally, cleanup all not needed for item in self.cleanup: if item in self: del self[item] def commit(self, tid=None, transaction=None, commit_phase=1, commit_mask=0xff): if not commit_phase & commit_mask: return self error = None drop = True devop = 'set' cleanup = [] if tid: transaction = self.global_tx[tid] else: if transaction: drop = False else: transaction = self.current_tx # ignore global rollbacks on invalid routes if self['ipdb_scope'] == 'create' and commit_phase > 1: return # create a new route if self['ipdb_scope'] != 'system': devop = 'add' # work on an existing route snapshot = self.pick() added, removed = transaction // snapshot added.pop('ipdb_scope', None) removed.pop('ipdb_scope', None) try: # route set if self['family'] != AF_MPLS: cleanup = [any(snapshot['metrics'].values()) and not any(added.get('metrics', {}).values()), any(snapshot['encap'].values()) and not any(added.get('encap', {}).values())] if any(added.values()) or \ any(cleanup) or \ removed.get('multipath', None) or \ devop == 'add': # prepare multipath target sync wlist = [] if transaction['multipath']: mplen = len(transaction['multipath']) if mplen == 1: # set up local targets for nh in transaction['multipath']: for key in ('gateway', 'oif', 'newdst'): if nh.get(key, None): self.set_target(key, nh[key]) wlist.append(key) mpt = None else: def mpcheck(mpset): return len(mpset) == mplen mpt = self['multipath'].set_target(mpcheck, True) else: mpt = None # prepare the anchor key to catch *possible* route update old_key = self.make_key(self) new_key = self.make_key(transaction) if old_key != new_key: # delete old record if devop == 'set': self.nl.route('del', **dict(old_key._asdict())) # assume we can not move routes between tables (yet ;) if self['family'] == AF_MPLS: route_index = self.ipdb.routes.tables['mpls'].idx else: route_index = (self.ipdb .routes .tables[self['table'] or 254] .idx) # re-link the route record if new_key in route_index: raise CommitException('route idx conflict') else: route_index[new_key] = {'key': new_key, 'route': self} # wipe the old key, if needed if old_key in route_index: del route_index[old_key] self.nl.route(devop, **transaction) transaction.wait_all_targets() for key in ('metrics', 'via'): if transaction[key] and transaction[key]._targets: transaction[key].wait_all_targets() if mpt is not None: mpt.wait(SYNC_TIMEOUT) if not mpt.is_set(): raise CommitException('multipath target is not set') self['multipath'].clear_target(mpt) for key in wlist: self.wait_target(key) # route removal if (transaction['ipdb_scope'] in ('shadow', 'remove')) or\ ((transaction['ipdb_scope'] == 'create') and commit_phase == 2): if transaction['ipdb_scope'] == 'shadow': with self._direct_state: self['ipdb_scope'] = 'locked' # create watchdog wd = self.ipdb.watchdog('RTM_DELROUTE', **self.wd_key(snapshot)) for route in self.nl.route('delete', **snapshot): self.ipdb.routes.load_netlink(route) wd.wait() if transaction['ipdb_scope'] == 'shadow': with self._direct_state: self['ipdb_scope'] = 'shadow' except Exception as e: if commit_phase == 1: ret = self.commit(transaction=snapshot, commit_phase=2, commit_mask=commit_mask) if isinstance(ret, Exception): error = ret else: error = e else: if drop: self.drop(transaction.uid) x = RuntimeError() x.cause = e raise x if drop and commit_phase == 1: self.drop(transaction.uid) if error is not None: error.transaction = transaction raise error self.ipdb.routes.gc() return self def remove(self): self['ipdb_scope'] = 'remove' return self def shadow(self): self['ipdb_scope'] = 'shadow' return self def detach(self): if self.get('family') == AF_MPLS: table = 'mpls' else: table = self.get('table', 254) del self.ipdb.routes.tables[table][self.make_key(self)] class Route(BaseRoute): _nested = ['encap', 'metrics'] wd_key = WatchdogKey @classmethod def make_encap(cls, encap): ''' Normalize encap object ''' labels = encap.get('labels', None) if isinstance(labels, (list, tuple, set)): labels = '/'.join(map(lambda x: str(x['label']) if isinstance(x, dict) else str(x), labels)) if not isinstance(labels, basestring): raise TypeError('labels struct not supported') return {'type': encap.get('type', 'mpls'), 'labels': labels} @classmethod def make_nh_key(cls, msg): ''' Construct from a netlink message a multipath nexthop key ''' values = [] if isinstance(msg, nlmsg_base): for field in IPNHKey._fields: v = msg.get_attr(msg.name2nla(field)) if field == 'encap': # 1. encap type if msg.get_attr('RTA_ENCAP_TYPE') != 1: # FIXME values.append(None) continue # 2. encap_type == 'mpls' v = '/'.join([str(x['label']) for x in v.get_attr('MPLS_IPTUNNEL_DST')]) elif v is None: v = msg.get(field, None) values.append(v) elif isinstance(msg, dict): for field in IPNHKey._fields: v = msg.get(field, None) if field == 'encap' and v and v['labels']: v = v['labels'] elif (field == 'encap') and \ (len(msg.get('multipath', []) or []) == 1): v = (tuple(msg['multipath'].raw.values())[0] .get('encap', {}) .get('labels', None)) elif field == 'encap': v = None elif (field == 'gateway') and \ (len(msg.get('multipath', []) or []) == 1) and \ not v: v = (tuple(msg['multipath'].raw.values())[0] .get('gateway', None)) if field == 'encap' and isinstance(v, (list, tuple, set)): v = '/'.join(map(lambda x: str(x['label']) if isinstance(x, dict) else str(x), v)) values.append(v) else: raise TypeError('prime not supported: %s' % type(msg)) return IPNHKey(*values) @classmethod def make_key(cls, msg): ''' Construct from a netlink message a key that can be used to locate the route in the table ''' values = [] if isinstance(msg, nlmsg_base): for field in RouteKey._fields: v = msg.get_attr(msg.name2nla(field)) if field == 'dst': if v is not None: v = '%s/%s' % (v, msg['dst_len']) else: v = 'default' elif field == 'tos' and msg.get('family') != AF_INET: # ignore tos field for non-IPv4 routes, # as it used as a key only there v = None elif v is None: v = msg.get(field, None) values.append(v) elif isinstance(msg, dict): for field in RouteKey._fields: v = msg.get(field, None) if field == 'dst' and \ isinstance(v, basestring) and \ v.find(':') > -1: v = v.split('/') ip = inet_ntop(AF_INET6, inet_pton(AF_INET6, v[0])) if len(v) > 1: v = '%s/%s' % (ip, v[1]) else: v = ip elif field == 'tos' and msg.get('family') != AF_INET: # ignore tos field for non-IPv4 routes, # as it used as a key only there v = None values.append(v) else: raise TypeError('prime not supported: %s' % type(msg)) return RouteKey(*values) def __setitem__(self, key, value): ret = value if (key in ('encap', 'metrics')) and isinstance(value, dict): # transactionals attach as is if type(value) in (Encap, Metrics): with self._direct_state: return Transactional.__setitem__(self, key, value) # check, if it exists already ret = Transactional.__getitem__(self, key) # it doesn't # (plain dict can be safely discarded) if (type(ret) == dict) or not ret: # bake transactionals in place if key == 'encap': ret = Encap(parent=self) elif key == 'metrics': ret = Metrics(parent=self) # attach transactional to the route with self._direct_state: Transactional.__setitem__(self, key, ret) # begin() works only if the transactional is attached if any(value.values()): if self._mode in ('implicit', 'explicit'): ret._begin(tid=self.current_tx.uid) [ret.__setitem__(k, v) for k, v in value.items() if v is not None] # corresponding transactional exists else: # set fields for k in ret: ret[k] = value.get(k, None) return elif key == 'multipath': cur = Transactional.__getitem__(self, key) if isinstance(cur, NextHopSet): # load entries vs = NextHopSet(value) for key in vs - cur: cur.add(key) for key in cur - vs: cur.remove(key) else: # drop any result of `update()` Transactional.__setitem__(self, key, NextHopSet(value)) return elif key == 'encap_type' and not isinstance(value, int): ret = encap_type.get(value, value) elif key == 'type' and not isinstance(value, int): ret = rt_type.get(value, value) elif key == 'proto' and not isinstance(value, int): ret = rt_proto.get(value, value) Transactional.__setitem__(self, key, ret) def __getitem__(self, key): ret = Transactional.__getitem__(self, key) if (key in ('encap', 'metrics', 'multipath')) and (ret is None): with self._direct_state: self[key] = [] if key == 'multipath' else {} ret = self[key] return ret class MPLSRoute(BaseRoute): wd_key = WatchdogMPLSKey _nested = ['via'] @classmethod def make_nh_key(cls, msg): ''' Construct from a netlink message a multipath nexthop key ''' return MPLSNHKey(newdst=tuple(msg['newdst']), via=msg.get('via', {}).get('addr', None), oif=msg.get('oif', None)) @classmethod def make_key(cls, msg): ''' Construct from a netlink message a key that can be used to locate the route in the table ''' ret = None if isinstance(msg, nlmsg): ret = msg.get_attr('RTA_DST') elif isinstance(msg, dict): ret = msg.get('dst', None) else: raise TypeError('prime not supported') if isinstance(ret, list): ret = ret[0]['label'] return ret def __setitem__(self, key, value): if key == 'via' and isinstance(value, dict): # replace with a new transactional if type(value) == Via: with self._direct_state: return BaseRoute.__setitem__(self, key, value) # or load the dict ret = BaseRoute.__getitem__(self, key) if not isinstance(ret, Via): ret = Via(parent=self) # attach new transactional -- replace any # non-Via object (may be a result of update()) with self._direct_state: BaseRoute.__setitem__(self, key, ret) # load value into the new object if any(value.values()): if self._mode in ('implicit', 'explicit'): ret._begin(tid=self.current_tx.uid) [ret.__setitem__(k, v) for k, v in value.items() if v is not None] else: # load value into existing object for k in ret: ret[k] = value.get(k, None) return elif key == 'multipath': cur = BaseRoute.__getitem__(self, key) if isinstance(cur, NextHopSet): # load entries vs = NextHopSet(value) for key in vs - cur: cur.add(key) for key in cur - vs: cur.remove(key) else: BaseRoute.__setitem__(self, key, NextHopSet(value)) else: BaseRoute.__setitem__(self, key, value) def __getitem__(self, key): with self._direct_state: ret = BaseRoute.__getitem__(self, key) if key == 'multipath' and ret is None: self[key] = [] ret = self[key] elif key == 'via' and ret is None: self[key] = {} ret = self[key] return ret class RoutingTable(object): route_class = Route def __init__(self, ipdb, prime=None): self.ipdb = ipdb self.lock = threading.Lock() self.idx = {} self.kdx = {} def __nogc__(self): return self.filter(lambda x: x['route']['ipdb_scope'] != 'gc') def __repr__(self): return repr([x['route'] for x in self.__nogc__()]) def __len__(self): return len(self.keys()) def __iter__(self): for record in self.__nogc__(): yield record['route'] def gc(self): now = time.time() for route in self.filter({'ipdb_scope': 'gc'}): if now - route['route']._gctime < 2: continue try: self.ipdb.nl.route('get', **route['route']) with route['route']._direct_state: route['route']['ipdb_scope'] = 'system' except: del self.idx[route['key']] def keys(self, key='dst'): with self.lock: return [x['route'][key] for x in self.__nogc__()] def items(self): for key in self.keys(): yield (key, self[key]) def filter(self, target, oneshot=False): # if isinstance(target, types.FunctionType): return filter(target, [x for x in self.idx.values()]) if isinstance(target, basestring): target = {'dst': target} if not isinstance(target, dict): raise TypeError('target type not supported: %s' % type(target)) ret = [] for record in self.idx.values(): for key, value in target.items(): if (key not in record['route']) or \ (value != record['route'][key]): break else: ret.append(record) if oneshot: return ret return ret def describe(self, target, forward=False): # match the route by index -- a bit meaningless, # but for compatibility if isinstance(target, int): keys = [x['key'] for x in self.__nogc__()] return self.idx[keys[target]] # match the route by key if isinstance(target, (tuple, list)): try: # full match return self.idx[RouteKey(*target)] except KeyError: # w/o iif and oif # when a route is just created, there can be no oif and # iif specified, if they weren't provided explicitly, # and in that case there will be the key w/o oif and # iif r = RouteKey._required l = RouteKey._fields try: return self.idx[RouteKey(*(target[:r] + (None, ) * (len(l) - r)))] except KeyError: # no luck with keys, fallback to simple search for key in self.idx: if key[:r] == target[:r]: return self.idx[key] else: raise if isinstance(target, nlmsg): return self.idx[Route.make_key(target)] # match the route by filter ret = self.filter(target, oneshot=True) if ret: return ret[0] if not forward: raise KeyError('record not found') # match the route by dict spec if not isinstance(target, dict): raise TypeError('lookups can be done only with dict targets') # split masks if target.get('dst', '').find('/') >= 0: dst = target['dst'].split('/') target['dst'] = dst[0] target['dst_len'] = int(dst[1]) if target.get('src', '').find('/') >= 0: src = target['src'].split('/') target['src'] = src[0] target['src_len'] = int(src[1]) # load and return the route, if exists route = Route(self.ipdb) ret = self.ipdb.nl.get_routes(**target) if not ret: raise KeyError('record not found') route.load_netlink(ret[0]) return {'route': route, 'key': None} def __delitem__(self, key): with self.lock: item = self.describe(key, forward=False) del self.idx[self.route_class.make_key(item['route'])] def load(self, msg): key = self.route_class.make_key(msg) self[key] = msg return key def __setitem__(self, key, value): with self.lock: try: record = self.describe(key, forward=False) except KeyError: record = {'route': self.route_class(self.ipdb), 'key': None} if isinstance(value, nlmsg): record['route'].load_netlink(value) elif isinstance(value, self.route_class): record['route'] = value elif isinstance(value, dict): with record['route']._direct_state: record['route'].update(value) key = self.route_class.make_key(record['route']) if record['key'] is None: self.idx[key] = {'route': record['route'], 'key': key} else: self.idx[key] = record if record['key'] != key: del self.idx[record['key']] record['key'] = key def __getitem__(self, key): with self.lock: return self.describe(key, forward=False)['route'] def __contains__(self, key): try: with self.lock: self.describe(key, forward=False) return True except KeyError: return False class MPLSTable(RoutingTable): route_class = MPLSRoute def keys(self): return self.idx.keys() def describe(self, target, forward=False): # match by key if isinstance(target, int): return self.idx[target] # match by rtmsg if isinstance(target, rtmsg): return self.idx[self.route_class.make_key(target)] raise KeyError('record not found') class RoutingTableSet(object): def __init__(self, ipdb): self.ipdb = ipdb self._gctime = time.time() self.ignore_rtables = ipdb._ignore_rtables or [] self.tables = {254: RoutingTable(self.ipdb)} self._event_map = {'RTM_NEWROUTE': self.load_netlink, 'RTM_DELROUTE': self.load_netlink, 'RTM_DELLINK': self.gc_mark_link, 'RTM_DELADDR': self.gc_mark_addr} def _register(self): for msg in self.ipdb.nl.get_routes(family=AF_INET): self.load_netlink(msg) for msg in self.ipdb.nl.get_routes(family=AF_INET6): self.load_netlink(msg) for msg in self.ipdb.nl.get_routes(family=AF_MPLS): self.load_netlink(msg) def add(self, spec=None, **kwarg): ''' Create a route from a dictionary ''' spec = dict(spec or kwarg) if 'tos' not in spec: spec['tos'] = 0 if 'scope' not in spec: spec['scope'] = 0 if 'table' not in spec: spec['table'] = 254 if 'family' not in spec: if (spec.get('dst', '').find(':') > -1) or \ (spec.get('gateway', '').find(':') > -1): spec['family'] = AF_INET6 else: spec['family'] = AF_INET if 'dst' not in spec: raise ValueError('dst not specified') if 'priority' not in spec: if spec['family'] == AF_INET6: spec['priority'] = IP6_RT_PRIO_USER else: spec['priority'] = None multipath = spec.pop('multipath', []) if spec.get('family', 0) == AF_MPLS: table = 'mpls' if table not in self.tables: self.tables[table] = MPLSTable(self.ipdb) route = MPLSRoute(self.ipdb) else: table = spec.get('table', 254) if table not in self.tables: self.tables[table] = RoutingTable(self.ipdb) route = Route(self.ipdb) route.update(spec) with route._direct_state: route['ipdb_scope'] = 'create' for nh in multipath: if 'encap' in nh: nh['encap'] = route.make_encap(nh['encap']) if table == 'mpls': nh['family'] = AF_MPLS route.add_nh(nh) route.begin() for (key, value) in spec.items(): if key == 'encap': route[key] = route.make_encap(value) else: route[key] = value self.tables[table][route.make_key(route)] = route return route def load_netlink(self, msg): ''' Loads an existing route from a rtmsg ''' if not isinstance(msg, rtmsg): return if msg['family'] == AF_MPLS: table = 'mpls' else: table = msg.get_attr('RTA_TABLE', msg['table']) if table in self.ignore_rtables: return now = time.time() if now - self._gctime > 5: self._gctime = now self.gc() # RTM_DELROUTE if msg['event'] == 'RTM_DELROUTE': try: # locate the record record = self.tables[table][msg] # delete the record if record['ipdb_scope'] not in ('locked', 'shadow'): del self.tables[table][msg] with record._direct_state: record['ipdb_scope'] = 'detached' except Exception as e: # just ignore this failure for now log.debug("delroute failed for %s", e) return # RTM_NEWROUTE if table not in self.tables: if table == 'mpls': self.tables[table] = MPLSTable(self.ipdb) else: self.tables[table] = RoutingTable(self.ipdb) key = self.tables[table].load(msg) return self.tables[table][key] def gc_mark_addr(self, msg): ## # Find invalid route records after addr delete # # Example:: # $ sudo ip link add test0 type dummy # $ sudo ip link set dev test0 up # $ sudo ip addr add 172.18.0.5/24 dev test0 # $ sudo ip route add 10.1.2.0/24 via 172.18.0.1 # ... # $ sudo ip addr flush dev test0 # # The route {'dst': '10.1.2.0/24', 'gateway': '172.18.0.1'} # will stay in the routing table being removed from the system. # That's because the kernel doesn't send route updates in that # case, so we have to calculate the update here -- or load all # the routes from scratch. The latter may be far too expensive. # Simply ignore secondary addresses, as they don't matter if msg['flags'] & IFA_F_SECONDARY: return # When the primary address is removed, corresponding routes # may be silently discarded. But if promote_secondaries is set # to 1, the next secondary becomes a new primary, and routes # stay. There is no way to know here, whether promote_secondaries # was set at the moment of the address removal, so we have to # act as if it wasn't. # Get the removed address: family = msg['family'] if family == AF_INET: addr = msg.get_attr('IFA_LOCAL') net = struct.unpack('>I', inet_pton(family, addr))[0] &\ (0xffffffff << (32 - msg['prefixlen'])) # now iterate all registered routes and mark those with # gateway from that network for record in self.filter({'family': family}): gw = record['route'].get('gateway') if gw: gwnet = struct.unpack('>I', inet_pton(family, gw))[0] & net if gwnet == net: with record['route']._direct_state: record['route']['ipdb_scope'] = 'gc' record['route']._gctime = time.time() elif family == AF_INET6: # TODO: add IPv6 support pass else: # ignore not (IPv4 or IPv6) return def gc_mark_link(self, msg): ### # mark route records for GC after link delete # if msg['family'] != 0: return for record in self.filter({'oif': msg['index']}): with record['route']._direct_state: record['route']['ipdb_scope'] = 'gc' record['route']._gctime = time.time() for record in self.filter({'iif': msg['index']}): with record['route']._direct_state: record['route']['ipdb_scope'] = 'gc' record['route']._gctime = time.time() def gc(self): for table in self.tables.keys(): self.tables[table].gc() def remove(self, route, table=None): if isinstance(route, Route): table = route.get('table', 254) or 254 route = route.get('dst', 'default') else: table = table or 254 self.tables[table][route].remove() def filter(self, target): # FIXME: turn into generator! ret = [] for table in self.tables.values(): if table is not None: ret.extend(table.filter(target)) return ret def describe(self, spec, table=254): return self.tables[table].describe(spec) 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] if (x.get('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): if key != value['dst']: raise ValueError("dst doesn't match key") return self.add(value) def __delitem__(self, key): return self.remove(key) def __repr__(self): return repr(self.tables[254]) spec = [{'name': 'routes', 'class': RoutingTableSet, 'kwarg': {}}] pyroute2-0.4.21/pyroute2/ipdb/utils.py0000664000175000017500000000042213130003470017513 0ustar peetpeet00000000000000import os import subprocess def test_reachable_icmp(host): with open(os.devnull, 'w') as devnull: return subprocess.check_call(['ping', '-c', '1', host], stdout=devnull, stderr=devnull) pyroute2-0.4.21/pyroute2/__init__.py0000664000175000017500000000737613152305337017226 0ustar peetpeet00000000000000## # # NB: the deferred import code may be removed # # That should not affect neither the public API, nor the # type matching with isinstance() and issubclass() # import sys import struct import logging from pyroute2.ipdb.exceptions import (DeprecationException, CommitException, CreateException, PartialCommitException) from pyroute2.netlink.exceptions import (NetlinkError, NetlinkDecodeError) from pyroute2.netlink.rtnl.req import (IPRouteRequest, IPLinkRequest) from pyroute2.iproute import (IPRoute, IPBatch, RawIPRoute) from pyroute2.ipset import IPSet from pyroute2.ipdb.main import IPDB from pyroute2.iwutil import IW from pyroute2.devlink import DL from pyroute2.netns.nslink import NetNS from pyroute2.netns.process.proxy import NSPopen from pyroute2.netlink.rtnl.iprsocket import IPRSocket from pyroute2.netlink.taskstats import TaskStats from pyroute2.netlink.nl80211 import NL80211 from pyroute2.netlink.devlink import DevlinkSocket from pyroute2.netlink.event.acpi_event import AcpiEventSocket from pyroute2.netlink.event.dquot import DQuotSocket from pyroute2.netlink.ipq import IPQSocket from pyroute2.netlink.diag import DiagSocket from pyroute2.netlink.generic import GenericNetlinkSocket from pyroute2.netlink.nfnetlink.nftables import NFTSocket # # The Console class is a bit special, it tries to engage # modules from stdlib, that are sometimes stripped. Some # of them are optional, but some aren't. So catch possible # errors here. try: from pyroute2.cli import Console HAS_CONSOLE = True except ImportError: HAS_CONSOLE = False log = logging.getLogger(__name__) # Add a NullHandler to the library's top-level logger to avoid complaints # on logging calls when no handler is configured. # see https://docs.python.org/2/howto/logging.html#library-config if sys.version_info >= (2, 7): # This is only available from 2.7 onwards log.addHandler(logging.NullHandler()) try: # probe, if the bytearray can be used in struct.unpack_from() struct.unpack_from('I', bytearray((1, 0, 0, 0)), 0) except: if sys.version_info[0] < 3: # monkeypatch for old Python versions log.warning('patching struct.unpack_from()') def wrapped(fmt, buf, offset=0): return struct._u_f_orig(fmt, str(buf), offset) struct._u_f_orig = struct.unpack_from struct.unpack_from = wrapped else: raise # reexport exceptions exceptions = [NetlinkError, NetlinkDecodeError, DeprecationException, CommitException, CreateException, PartialCommitException] # reexport classes classes = [IPRouteRequest, IPLinkRequest, IPRoute, IPBatch, RawIPRoute, IPSet, IPDB, IW, DL, NetNS, NSPopen, IPRSocket, TaskStats, NL80211, DevlinkSocket, AcpiEventSocket, DQuotSocket, IPQSocket, DiagSocket, GenericNetlinkSocket, NFTSocket] if HAS_CONSOLE: classes.append(Console) else: log.warning("Couldn't import the Console class") __all__ = [] class __common(object): def __getattribute__(self, key): log.warning('module pyroute2.ipdb.common is deprecated, ' 'use pyroute2.ipdb.exceptions instead') return getattr(globals()['ipdb'].exceptions, key) globals()['ipdb'].common = __common() __all__.extend([x.__name__ for x in exceptions]) __all__.extend([x.__name__ for x in classes]) pyroute2-0.4.21/pyroute2/config/0000775000175000017500000000000013152333366016350 5ustar peetpeet00000000000000pyroute2-0.4.21/pyroute2/config/test_platform.py0000664000175000017500000001736613123443516021617 0ustar peetpeet00000000000000''' Platform tests to discover the system capabilities. ''' import os import sys import select import struct import threading from pyroute2 import config from pyroute2.common import uifname from pyroute2 import RawIPRoute from pyroute2.netlink.rtnl import RTNLGRP_LINK class SkipTest(Exception): pass class TestCapsRtnl(object): ''' A minimal test set to collect the RTNL implementation capabilities. It uses raw RTNL sockets and doesn't run any proxy code, so no transparent helpers are executed -- e.g., it will not create bridge via `brctl`, if RTNL doesn't support it. A short developer's guide:: def test_whatever_else(self): code This test will create a capability record `whatever_else`. If the `code` fails, the `whatever_else` will be set to `False`. If it throws the `SkipTest` exception, the `whatever_else` will be set to `None`. Otherwise it will be set to whatever the test returns. To collect the capabilities:: tce = TestCapsExt() tce.collect() print(tce.capabilities) Collected capabilities are in the `TestCapsExt.capabilities` dictionary, you can use them directly or by setting the `config.capabilities` singletone:: from pyroute2 import config # ... tce.collect() config.capabilities = tce.capabilities ''' def __init__(self): self.capabilities = {} self.ifnames = [] self.rtm_newlink = {} self.rtm_dellink = {} self.rtm_events = {} self.cmd, self.cmdw = os.pipe() self.ip = None self.event = threading.Event() def __getitem__(self, key): return self.capabilities[key] def set_capability(self, key, value): ''' Set a capability. ''' self.capabilities[key] = value def ifname(self): ''' Register and return a new unique interface name to be used in a test. ''' ifname = uifname() self.ifnames.append(ifname) self.rtm_events[ifname] = threading.Event() self.rtm_newlink[ifname] = [] self.rtm_dellink[ifname] = [] return ifname def monitor(self): # The monitoring code to collect RTNL messages # asynchronously. # Do **NOT** run manually. # use a separate socket for monitoring ip = RawIPRoute() ip.bind(RTNLGRP_LINK) poll = select.poll() poll.register(ip, select.POLLIN | select.POLLPRI) poll.register(self.cmd, select.POLLIN | select.POLLPRI) self.event.set() while True: events = poll.poll() for (fd, evt) in events: if fd == ip.fileno(): msgs = ip.get() for msg in msgs: name = msg.get_attr('IFLA_IFNAME') event = msg.get('event') if name not in self.rtm_events: continue if event == 'RTM_NEWLINK': self.rtm_events[name].set() self.rtm_newlink[name].append(msg) elif event == 'RTM_DELLINK': self.rtm_dellink[name].append(msg) else: ip.close() return def setup(self): # The setup procedure for a test. # Do **NOT** run manually. # create the raw socket self.ip = RawIPRoute() def teardown(self): # The teardown procedure for a test. # Do **NOT** run manually. # clear the collected interfaces for ifname in self.ifnames: self.rtm_events[ifname].wait() self.rtm_events[ifname].clear() if self.rtm_newlink.get(ifname): self.ip.link('del', index=self.rtm_newlink[ifname][0]['index']) self.ifnames = [] # close the socket self.ip.close() def collect(self): ''' Run the tests and collect the capabilities. They will be saved in the `TestCapsRtnl.capabilities` attribute. ''' symbols = sorted(dir(self)) # start the monitoring thread mthread = threading.Thread(target=self.monitor) mthread.start() self.event.wait() # wait for the thread setup for name in symbols: if name.startswith('test_'): self.setup() try: ret = getattr(self, name)() if ret is None: ret = True self.set_capability(name[5:], ret) except SkipTest: self.set_capability(name[5:], None) except Exception: for ifname in self.ifnames: # cancel events queued for that test self.rtm_events[ifname].set() self.set_capability(name[5:], False) self.teardown() # stop the monitor os.write(self.cmdw, b'q') mthread.join() return self.capabilities def test_uname(self): ''' Return collected uname ''' return config.uname def test_python_version(self): ''' Return Python version ''' return sys.version def test_unpack_from(self): ''' Does unpack_from() support bytearray as the buffer ''' # probe unpack from try: struct.unpack_from('I', bytearray((1, 0, 0, 0)), 0) except: return False # works... but may it be monkey patched? if hasattr(struct, '_u_f_orig'): return False def test_create_dummy(self): ''' An obvious test: an ability to create dummy interfaces ''' self.ghost = self.ifname() self.ip.link('add', ifname=self.ghost, kind='dummy') def test_create_bridge(self): ''' Can the kernel create bridges via netlink? ''' self.ip.link('add', ifname=self.ifname(), kind='bridge') def test_create_bond(self): ''' Can the kernel create bonds via netlink? ''' self.ip.link('add', ifname=self.ifname(), kind='bond') def test_ghost_newlink_count(self): ''' A normal flow (req == request, brd == broadcast message):: (req) -> RTM_NEWLINK (brd) <- RTM_NEWLINK (req) -> RTM_DELLINK (brd) <- RTM_DELLINK But on old kernels you can encounter the following:: (req) -> RTM_NEWLINK (brd) <- RTM_NEWLINK (req) -> RTM_DELLINK (brd) <- RTM_DELLINK (brd) <- RTM_NEWLINK (!) false positive And that obviously can break the code that relies on broadcast updates, since it will see as a new interface is created immediately after it was destroyed. One can ignore RTM_NEWLINK for the same name that follows a normal RTM_DELLINK. To do that, one should be sure the message will come. Another question is how many messages to ignore. This is not a test s.str., but it should follow after the `test_create_dummy`. It counts, how many RTM_NEWLINK messages arrived during the `test_create_dummy`. The ghost newlink messages count will be the same for other interface types as well. ''' with open('/proc/version', 'r') as f: if int(f.read().split()[2][0]) > 2: # the issue is reported only for kernels 2.x return 0 # there is no guarantee it will come; it *may* come self.rtm_events[self.ghost].wait(0.5) return max(len(self.rtm_newlink.get(self.ghost, [])) - 1, 0) pyroute2-0.4.21/pyroute2/config/eventlet.py0000664000175000017500000000040113127143031020531 0ustar peetpeet00000000000000import logging from pyroute2.config.asyncio import asyncio_config log = logging.getLogger(__name__) log.warning("Please use pyroute2.config.asyncio.asyncio_config") log.warning("The eventlet module will be dropped soon ") eventlet_config = asyncio_config pyroute2-0.4.21/pyroute2/config/__init__.py0000664000175000017500000000127213152305337020460 0ustar peetpeet00000000000000import socket import platform import multiprocessing from distutils.version import LooseVersion SocketBase = socket.socket MpPipe = multiprocessing.Pipe MpQueue = multiprocessing.Queue MpProcess = multiprocessing.Process ipdb_nl_async = True commit_barrier = 0 # save uname() on startup time: it is not so # highly possible that the kernel will be # changed in runtime, while calling uname() # every time is a bit expensive uname = platform.uname() machine = platform.machine() arch = platform.architecture()[0] kernel = LooseVersion(uname[2]).version[:3] AF_BRIDGE = getattr(socket, 'AF_BRIDGE', 7) AF_NETLINK = getattr(socket, 'AF_NETLINK', 16) data_plugins_pkgs = [] data_plugins_path = [] pyroute2-0.4.21/pyroute2/config/asyncio.py0000664000175000017500000000434113127143031020357 0ustar peetpeet00000000000000# # Author: Angus Lees # # Backported from a Neutron privsep proposal with the # permission of the author. # from __future__ import absolute_import import functools import socket import types try: import cPickle as pickle except ImportError: import pickle from pyroute2 import config as config _socketmethods = ( 'bind', 'close', 'connect', 'connect_ex', 'listen', 'getpeername', 'getsockname', 'getsockopt', 'makefile', 'recv', 'recvfrom', 'recv_into', 'recvfrom_into', 'send', 'sendto', 'sendall', 'setsockopt', 'setblocking', 'settimeout', 'gettimeout', 'shutdown') def _forward(name, self, *args, **kwargs): return getattr(self._sock, name)(*args, **kwargs) class _SocketWrapper(object): """eventlet-monkeypatch friendly socket class""" def __init__(self, *args, **kwargs): _sock = kwargs.get('_sock', None) or socket.socket(*args, **kwargs) self._sock = _sock for name in _socketmethods: f = functools.partial(_forward, name) f.__name__ = name setattr(_SocketWrapper, name, types.MethodType(f, self)) def fileno(self): return self._sock.fileno() def dup(self): return self.__class__(_sock=self._sock.dup()) class _MpConnection(object): """Highly limited multiprocessing.Connection alternative""" def __init__(self, sock): sock.setblocking(True) self.sock = sock def fileno(self): return self.sock.fileno() def send(self, obj): pickle.dump(obj, self, protocol=-1) def write(self, s): self.sock.sendall(s) def recv(self): return pickle.load(self) def read(self, n): return self.sock.recv(n) def readline(self): buf = b'' c = None while c != b'\n': c = self.sock.recv(1) buf += c return buf def close(self): self.sock.close() def _MultiprocessingPipe(): """multiprocess.Pipe reimplementation that uses MpConnection wrapper""" s1, s2 = socket.socketpair() return (_MpConnection(s1), _MpConnection(s2)) def asyncio_config(): config.SocketBase = _SocketWrapper config.MpPipe = _MultiprocessingPipe config.ipdb_nl_async = False pyroute2-0.4.21/pyroute2/ipset.py0000664000175000017500000004217113152331050016572 0ustar peetpeet00000000000000''' IPSet module ============ ipset support. This module is tested with hash:ip, hash:net, list:set and several other ipset structures (like hash:net,iface). There is no guarantee that this module is working with all available ipset modules. It supports almost all kernel commands (create, destroy, flush, rename, swap, test...) ''' import errno import socket from pyroute2.netlink import NLMSG_ERROR from pyroute2.netlink import NLM_F_REQUEST from pyroute2.netlink import NLM_F_DUMP from pyroute2.netlink import NLM_F_ACK from pyroute2.netlink import NLM_F_EXCL from pyroute2.netlink import NETLINK_NETFILTER from pyroute2.netlink.exceptions import NetlinkError, IPSetError from pyroute2.netlink.nlsocket import NetlinkSocket from pyroute2.netlink.nfnetlink import NFNL_SUBSYS_IPSET from pyroute2.netlink.nfnetlink.ipset import IPSET_CMD_PROTOCOL from pyroute2.netlink.nfnetlink.ipset import IPSET_CMD_CREATE from pyroute2.netlink.nfnetlink.ipset import IPSET_CMD_DESTROY from pyroute2.netlink.nfnetlink.ipset import IPSET_CMD_SWAP from pyroute2.netlink.nfnetlink.ipset import IPSET_CMD_LIST from pyroute2.netlink.nfnetlink.ipset import IPSET_CMD_ADD from pyroute2.netlink.nfnetlink.ipset import IPSET_CMD_DEL from pyroute2.netlink.nfnetlink.ipset import IPSET_CMD_FLUSH from pyroute2.netlink.nfnetlink.ipset import IPSET_CMD_RENAME from pyroute2.netlink.nfnetlink.ipset import IPSET_CMD_TEST from pyroute2.netlink.nfnetlink.ipset import IPSET_CMD_TYPE from pyroute2.netlink.nfnetlink.ipset import IPSET_CMD_HEADER from pyroute2.netlink.nfnetlink.ipset import ipset_msg from pyroute2.netlink.nfnetlink.ipset import IPSET_FLAG_WITH_COUNTERS from pyroute2.netlink.nfnetlink.ipset import IPSET_FLAG_WITH_COMMENT from pyroute2.netlink.nfnetlink.ipset import IPSET_FLAG_WITH_FORCEADD from pyroute2.netlink.nfnetlink.ipset import IPSET_DEFAULT_MAXELEM from pyroute2.netlink.nfnetlink.ipset import IPSET_ERR_PROTOCOL from pyroute2.netlink.nfnetlink.ipset import IPSET_ERR_FIND_TYPE from pyroute2.netlink.nfnetlink.ipset import IPSET_ERR_MAX_SETS from pyroute2.netlink.nfnetlink.ipset import IPSET_ERR_BUSY from pyroute2.netlink.nfnetlink.ipset import IPSET_ERR_EXIST_SETNAME2 from pyroute2.netlink.nfnetlink.ipset import IPSET_ERR_TYPE_MISMATCH from pyroute2.netlink.nfnetlink.ipset import IPSET_ERR_EXIST from pyroute2.netlink.nfnetlink.ipset import IPSET_ERR_INVALID_CIDR from pyroute2.netlink.nfnetlink.ipset import IPSET_ERR_INVALID_NETMASK from pyroute2.netlink.nfnetlink.ipset import IPSET_ERR_INVALID_FAMILY from pyroute2.netlink.nfnetlink.ipset import IPSET_ERR_TIMEOUT from pyroute2.netlink.nfnetlink.ipset import IPSET_ERR_REFERENCED from pyroute2.netlink.nfnetlink.ipset import IPSET_ERR_IPADDR_IPV4 from pyroute2.netlink.nfnetlink.ipset import IPSET_ERR_IPADDR_IPV6 from pyroute2.netlink.nfnetlink.ipset import IPSET_ERR_COUNTER from pyroute2.netlink.nfnetlink.ipset import IPSET_ERR_COMMENT from pyroute2.netlink.nfnetlink.ipset import IPSET_ERR_INVALID_MARKMASK from pyroute2.netlink.nfnetlink.ipset import IPSET_ERR_SKBINFO def _nlmsg_error(msg): return msg['header']['type'] == NLMSG_ERROR class IPSet(NetlinkSocket): ''' NFNetlink socket (family=NETLINK_NETFILTER). Implements API to the ipset functionality. ''' policy = {IPSET_CMD_PROTOCOL: ipset_msg, IPSET_CMD_LIST: ipset_msg, IPSET_CMD_TYPE: ipset_msg, IPSET_CMD_HEADER: ipset_msg} def __init__(self, version=6, attr_revision=None, nfgen_family=2): super(IPSet, self).__init__(family=NETLINK_NETFILTER) policy = dict([(x | (NFNL_SUBSYS_IPSET << 8), y) for (x, y) in self.policy.items()]) self.register_policy(policy) self._proto_version = version self._attr_revision = attr_revision self._nfgen_family = nfgen_family def request(self, msg, msg_type, msg_flags=NLM_F_REQUEST | NLM_F_DUMP, terminate=None): msg['nfgen_family'] = self._nfgen_family try: return self.nlm_request(msg, msg_type | (NFNL_SUBSYS_IPSET << 8), msg_flags, terminate=terminate) except NetlinkError as err: raise _IPSetError(err.code, cmd=msg_type) def headers(self, name): ''' Get headers of the named ipset. It can be used to test if one ipset exists, since it returns a no such file or directory. ''' return self._list_or_headers(IPSET_CMD_HEADER, name=name) def list(self, *argv, **kwarg): ''' List installed ipsets. If `name` is provided, list the named ipset or return an empty list. Be warned: netlink does not return an error if given name does not exit, you will receive an empty list. ''' if len(argv): kwarg['name'] = argv[0] return self._list_or_headers(IPSET_CMD_LIST, **kwarg) def _list_or_headers(self, cmd, name=None): msg = ipset_msg() msg['attrs'] = [['IPSET_ATTR_PROTOCOL', self._proto_version]] if name is not None: msg['attrs'].append(['IPSET_ATTR_SETNAME', name]) return self.request(msg, cmd) def destroy(self, name=None): ''' Destroy one or all ipset (when name is None) ''' msg = ipset_msg() msg['attrs'] = [['IPSET_ATTR_PROTOCOL', self._proto_version]] if name is not None: msg['attrs'].append(['IPSET_ATTR_SETNAME', name]) return self.request(msg, IPSET_CMD_DESTROY, msg_flags=NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL, terminate=_nlmsg_error) def create(self, name, stype='hash:ip', family=socket.AF_INET, exclusive=True, counters=False, comment=False, maxelem=IPSET_DEFAULT_MAXELEM, forceadd=False, hashsize=None, timeout=None): ''' Create an ipset `name` of type `stype`, by default `hash:ip`. Common ipset options are supported: * exclusive -- if set, raise an error if the ipset exists * counters -- enable data/packets counters * comment -- enable comments capability * maxelem -- max size of the ipset * forceadd -- you should refer to the ipset manpage * hashsize -- size of the hashtable (if any) * timeout -- enable and set a default value for entries (if not None) ''' excl_flag = NLM_F_EXCL if exclusive else 0 msg = ipset_msg() cadt_flags = 0 if counters: cadt_flags |= IPSET_FLAG_WITH_COUNTERS if comment: cadt_flags |= IPSET_FLAG_WITH_COMMENT if forceadd: cadt_flags |= IPSET_FLAG_WITH_FORCEADD data = {"attrs": [["IPSET_ATTR_CADT_FLAGS", cadt_flags], ["IPSET_ATTR_MAXELEM", maxelem]]} if hashsize is not None: data['attrs'] += [["IPSET_ATTR_HASHSIZE", hashsize]] if timeout is not None: data['attrs'] += [["IPSET_ATTR_TIMEOUT", timeout]] if self._attr_revision is None: # Get the last revision supported by kernel revision = self.get_supported_revisions(stype)[1] else: revision = self._attr_revision msg['attrs'] = [['IPSET_ATTR_PROTOCOL', self._proto_version], ['IPSET_ATTR_SETNAME', name], ['IPSET_ATTR_TYPENAME', stype], ['IPSET_ATTR_FAMILY', family], ['IPSET_ATTR_REVISION', revision], ["IPSET_ATTR_DATA", data]] return self.request(msg, IPSET_CMD_CREATE, msg_flags=NLM_F_REQUEST | NLM_F_ACK | excl_flag, terminate=_nlmsg_error) def _entry_to_data_attrs(self, entry, etype, family): attrs = [] if family is not None: if family == socket.AF_INET: ip_version = 'IPSET_ATTR_IPADDR_IPV4' elif family == socket.AF_INET6: ip_version = 'IPSET_ATTR_IPADDR_IPV6' elif family == socket.AF_UNSPEC: ip_version = None else: raise TypeError('unknown family') for e, t in zip(entry.split(','), etype.split(',')): if t in ('ip', 'net'): if t == 'net': if '/' in e: e, cidr = e.split('/') attrs += [['IPSET_ATTR_CIDR', int(cidr)]] elif '-' in e: e, to = e.split('-') attrs += [['IPSET_ATTR_IP_TO', {'attrs': [[ip_version, to]]}]] attrs += [['IPSET_ATTR_IP_FROM', {'attrs': [[ip_version, e]]}]] elif t == 'iface': attrs += [['IPSET_ATTR_IFACE', e]] elif t == 'mark': attrs += [['IPSET_ATTR_MARK', int(e)]] elif t == 'set': attrs += [['IPSET_ATTR_NAME', e]] elif t == "mac": attrs += [['IPSET_ATTR_ETHER', e]] return attrs def _add_delete_test(self, name, entry, family, cmd, exclusive, comment=None, timeout=None, etype="ip", packets=None, bytes=None): excl_flag = NLM_F_EXCL if exclusive else 0 data_attrs = self._entry_to_data_attrs(entry, etype, family) if comment is not None: data_attrs += [["IPSET_ATTR_COMMENT", comment], ["IPSET_ATTR_CADT_LINENO", 0]] if timeout is not None: data_attrs += [["IPSET_ATTR_TIMEOUT", timeout]] if bytes is not None: data_attrs += [["IPSET_ATTR_BYTES", bytes]] if packets is not None: data_attrs += [["IPSET_ATTR_PACKETS", packets]] msg = ipset_msg() msg['attrs'] = [['IPSET_ATTR_PROTOCOL', self._proto_version], ['IPSET_ATTR_SETNAME', name], ['IPSET_ATTR_DATA', {'attrs': data_attrs}]] return self.request(msg, cmd, msg_flags=NLM_F_REQUEST | NLM_F_ACK | excl_flag, terminate=_nlmsg_error) def add(self, name, entry, family=socket.AF_INET, exclusive=True, comment=None, timeout=None, etype="ip", **kwargs): ''' Add a member to the ipset. etype is the entry type that you add to the ipset. It's related to the ipset type. For example, use "ip" for one hash:ip or bitmap:ip ipset. When your ipset store a tuple, like "hash:net,iface", you must use a comma a separator (etype="net,iface") ''' return self._add_delete_test(name, entry, family, IPSET_CMD_ADD, exclusive, comment=comment, timeout=timeout, etype=etype, **kwargs) def delete(self, name, entry, family=socket.AF_INET, exclusive=True, etype="ip"): ''' Delete a member from the ipset. See add method for more information on etype. ''' return self._add_delete_test(name, entry, family, IPSET_CMD_DEL, exclusive, etype=etype) def test(self, name, entry, family=socket.AF_INET, etype="ip"): ''' Test if a member is part of an ipset See add method for more information on etype. ''' try: self._add_delete_test(name, entry, family, IPSET_CMD_TEST, False, etype=etype) return True except IPSetError as e: if e.code == IPSET_ERR_EXIST: return False raise e def swap(self, set_a, set_b): ''' Swap two ipsets ''' msg = ipset_msg() msg['attrs'] = [['IPSET_ATTR_PROTOCOL', self._proto_version], ['IPSET_ATTR_SETNAME', set_a], ['IPSET_ATTR_TYPENAME', set_b]] return self.request(msg, IPSET_CMD_SWAP, msg_flags=NLM_F_REQUEST | NLM_F_ACK, terminate=_nlmsg_error) def flush(self, name=None): ''' Flush all ipsets. When name is set, flush only this ipset. ''' msg = ipset_msg() msg['attrs'] = [['IPSET_ATTR_PROTOCOL', self._proto_version]] if name is not None: msg['attrs'].append(['IPSET_ATTR_SETNAME', name]) return self.request(msg, IPSET_CMD_FLUSH, msg_flags=NLM_F_REQUEST | NLM_F_ACK, terminate=_nlmsg_error) def rename(self, name_src, name_dst): ''' Rename the ipset. ''' msg = ipset_msg() msg['attrs'] = [['IPSET_ATTR_PROTOCOL', self._proto_version], ['IPSET_ATTR_SETNAME', name_src], ['IPSET_ATTR_TYPENAME', name_dst]] return self.request(msg, IPSET_CMD_RENAME, msg_flags=NLM_F_REQUEST | NLM_F_ACK, terminate=_nlmsg_error) def get_supported_revisions(self, stype, family=socket.AF_INET): ''' Return minimum and maximum of revisions supported by the kernel ''' msg = ipset_msg() msg['attrs'] = [['IPSET_ATTR_PROTOCOL', self._proto_version], ['IPSET_ATTR_TYPENAME', stype], ['IPSET_ATTR_FAMILY', family]] response = self.request(msg, IPSET_CMD_TYPE, msg_flags=NLM_F_REQUEST | NLM_F_ACK, terminate=_nlmsg_error) min_revision = response[0].get_attr("IPSET_ATTR_PROTOCOL_MIN") max_revision = response[0].get_attr("IPSET_ATTR_REVISION") return min_revision, max_revision class _IPSetError(IPSetError): ''' Proxy class to not import all specifics ipset code in exceptions.py Out of the ipset module, a caller should use parent class instead ''' def __init__(self, code, msg=None, cmd=None): if code in self.base_map: msg = self.base_map[code] elif cmd in self.cmd_map: error_map = self.cmd_map[cmd] if code in error_map: msg = error_map[code] super(_IPSetError, self).__init__(code, msg) base_map = {IPSET_ERR_PROTOCOL: "Kernel error received:" " ipset protocol error", IPSET_ERR_INVALID_CIDR: "The value of the CIDR parameter of" " the IP address is invalid", IPSET_ERR_TIMEOUT: "Timeout cannot be used: set was created" " without timeout support", IPSET_ERR_IPADDR_IPV4: "An IPv4 address is expected, but" " not received", IPSET_ERR_IPADDR_IPV6: "An IPv6 address is expected, but" " not received", IPSET_ERR_COUNTER: "Packet/byte counters cannot be used:" " set was created without counter support", IPSET_ERR_COMMENT: "Comment string is too long!", IPSET_ERR_SKBINFO: "Skbinfo mapping cannot be used: " " set was created without skbinfo support"} c_map = {errno.EEXIST: "Set cannot be created: set with the same" " name already exists", IPSET_ERR_FIND_TYPE: "Kernel error received: " "set type not supported", IPSET_ERR_MAX_SETS: "Kernel error received: maximal number of" " sets reached, cannot create more.", IPSET_ERR_INVALID_NETMASK: "The value of the netmask parameter" " is invalid", IPSET_ERR_INVALID_MARKMASK: "The value of the markmask parameter" " is invalid", IPSET_ERR_INVALID_FAMILY: "Protocol family not supported by the" " set type"} destroy_map = {IPSET_ERR_BUSY: "Set cannot be destroyed: it is in use" " by a kernel component"} r_map = {IPSET_ERR_EXIST_SETNAME2: "Set cannot be renamed: a set with the" " new name already exists", IPSET_ERR_REFERENCED: "Set cannot be renamed: it is in use by" " another system"} s_map = {IPSET_ERR_EXIST_SETNAME2: "Sets cannot be swapped: the second set" " does not exist", IPSET_ERR_TYPE_MISMATCH: "The sets cannot be swapped: their type" " does not match"} a_map = {IPSET_ERR_EXIST: "Element cannot be added to the set: it's" " already added"} del_map = {IPSET_ERR_EXIST: "Element cannot be deleted from the set:" " it's not added"} cmd_map = {IPSET_CMD_CREATE: c_map, IPSET_CMD_DESTROY: destroy_map, IPSET_CMD_RENAME: r_map, IPSET_CMD_SWAP: s_map, IPSET_CMD_ADD: a_map, IPSET_CMD_DEL: del_map} pyroute2-0.4.21/pyroute2/dhcp/0000775000175000017500000000000013152333366016021 5ustar peetpeet00000000000000pyroute2-0.4.21/pyroute2/dhcp/__init__.py0000664000175000017500000002320513123443516020131 0ustar peetpeet00000000000000''' DHCP protocol ============= The DHCP implementation here is far from complete, but already provides some basic functionality. Later it will be extended with IPv6 support and more DHCP options will be added. Right now it can be interesting mostly to developers, but not users and/or system administrators. So, the development hints first. The packet structure description is intentionally implemented as for netlink packets. Later these two parsers, netlink and generic, can be merged, so the syntax is more or less compatible. Packet fields ------------- There are two big groups of items within any DHCP packet. First, there are BOOTP/DHCP packet fields, they're defined with the `fields` attribute:: class dhcp4msg(msg): fields = ((name, format, policy), (name, format, policy), ... (name, format, policy)) The `name` can be any literal. Format should be specified as for the struct module, like `B` for `uint8`, or `i` for `int32`, or `>Q` for big-endian uint64. There are also aliases defined, so one can write `uint8` or `be16`, or like that. Possible aliases can be seen in the `pyroute2.protocols` module. The `policy` is a bit complicated. It can be a number or literal, and it will mean that it is a default value, that should be encoded if no other value is given. But when the `policy` is a dictionary, it can contain keys as follows:: 'l2addr': {'format': '6B', 'decode': ..., 'encode': ...} Keys `encode` and `decode` should contain filters to be used in decoding and encoding procedures. The encoding filter should accept the value from user's definition and should return a value that can be packed using `format`. The decoding filter should accept a value, decoded according to `format`, and should return value that can be used by a user. The `struct` module can not decode IP addresses etc, so they should be decoded as `4s`, e.g. Further transformation from 4 bytes string to a string like '10.0.0.1' performs the filter. DHCP options ------------ DHCP options are described in a similar way:: options = ((code, name, format), (code, name, format), ... (code, name, format)) Code is a `uint8` value, name can be any string literal. Format is a string, that must have a corresponding class, inherited from `pyroute2.dhcp.option`. One can find these classes in `pyroute2.dhcp` (more generic) or in `pyroute2.dhcp.dhcp4msg` (IPv4-specific). The option class must reside within dhcp message class. Every option class can be decoded in two ways. If it has fixed width fields, it can be decoded with ordinary `msg` routines, and in this case it can look like that:: class client_id(option): fields = (('type', 'uint8'), ('key', 'l2addr')) If it must be decoded by some custom rules, one can define the policy just like for the fields above:: class array8(option): policy = {'format': 'string', 'encode': lambda x: array('B', x).tobytes(), 'decode': lambda x: array('B', x).tolist()} In the corresponding modules, like in `pyroute2.dhcp.dhcp4msg`, one can define as many custom DHCP options, as one need. Just be sure, that they are compatible with the DHCP server and all fit into 1..254 (`uint8`) -- the 0 code is used for padding and the code 255 is the end of options code. ''' import sys import struct from array import array from pyroute2.common import basestring from pyroute2.protocols import msg BOOTREQUEST = 1 BOOTREPLY = 2 DHCPDISCOVER = 1 DHCPOFFER = 2 DHCPREQUEST = 3 DHCPDECLINE = 4 DHCPACK = 5 DHCPNAK = 6 DHCPRELEASE = 7 DHCPINFORM = 8 if not hasattr(array, 'tobytes'): # Python2 and Python3 versions of array differ, # but we need here a consistent API w/o warnings class array(array): tobytes = array.tostring class option(msg): code = 0 data_length = 0 policy = None value = None def __init__(self, content=None, buf=b'', offset=0, value=None, code=0): msg.__init__(self, content=content, buf=buf, offset=offset, value=value) self.code = code @property def length(self): if self.data_length is None: return None if self.data_length == 0: return 1 else: return self.data_length + 2 def encode(self): # pack code self.buf += struct.pack('B', self.code) if self.code in (0, 255): return self # save buf save = self.buf self.buf = b'' # pack data into the new buf if self.policy is not None: value = self.policy.get('encode', lambda x: x)(self.value) if self.policy['format'] == 'string': fmt = '%is' % len(value) else: fmt = self.policy['format'] if sys.version_info[0] == 3 and isinstance(value, str): value = value.encode('utf-8') self.buf = struct.pack(fmt, value) else: msg.encode(self) # get the length data = self.buf self.buf = save self.buf += struct.pack('B', len(data)) # attach the packed data self.buf += data return self def decode(self): if self.policy is not None: self.data_length = struct.unpack('B', self.buf[self.offset + 1: self.offset + 2])[0] if self.policy['format'] == 'string': fmt = '%is' % self.data_length else: fmt = self.policy['format'] value = struct.unpack(fmt, self.buf[self.offset + 2: self.offset + 2 + self.data_length]) if len(value) == 1: value = value[0] value = self.policy.get('decode', lambda x: x)(value) if isinstance(value, basestring) and \ self.policy['format'] == 'string': value = value[:value.find('\x00')] self.value = value else: msg.decode(self) return self class dhcpmsg(msg): options = () l2addr = None _encode_map = {} _decode_map = {} def _register_options(self): for option in self.options: code, name, fmt = option[:3] self._decode_map[code] =\ self._encode_map[name] = {'name': name, 'code': code, 'format': fmt} def decode(self): msg.decode(self) self._register_options() self['options'] = {} while self.offset < len(self.buf): code = struct.unpack('B', self.buf[self.offset:self.offset + 1])[0] if code == 0: self.offset += 1 continue if code == 255: return self # code is unknown -- bypass it if code not in self._decode_map: length = struct.unpack('B', self.buf[self.offset + 1: self.offset + 2])[0] self.offset += length + 2 continue # code is known, work on it option_class = getattr(self, self._decode_map[code]['format']) option = option_class(buf=self.buf, offset=self.offset) option.decode() self.offset += option.length if option.value is not None: value = option.value else: value = option self['options'][self._decode_map[code]['name']] = value return self def encode(self): msg.encode(self) self._register_options() # put message type options = self.get('options') or {'message_type': DHCPDISCOVER, 'parameter_list': [1, 3, 6, 12, 15, 28]} self.buf += self.uint8(code=53, value=options['message_type']).encode().buf self.buf += self.client_id({'type': 1, 'key': self['chaddr']}, code=61).encode().buf self.buf += self.string(code=60, value='pyroute2').encode().buf for (name, value) in options.items(): if name in ('message_type', 'client_id', 'vendor_id'): continue fmt = self._encode_map.get(name, {'format': None})['format'] if fmt is None: continue # name is known, ok option_class = getattr(self, fmt) if isinstance(value, dict): option = option_class(value, code=self._encode_map[name]['code']) else: option = option_class(code=self._encode_map[name]['code'], value=value) self.buf += option.encode().buf self.buf += self.none(code=255).encode().buf return self class none(option): pass class be16(option): policy = {'format': '>H'} class be32(option): policy = {'format': '>I'} class uint8(option): policy = {'format': 'B'} class string(option): policy = {'format': 'string'} class array8(option): policy = {'format': 'string', 'encode': lambda x: array('B', x).tobytes(), 'decode': lambda x: array('B', x).tolist()} class client_id(option): fields = (('type', 'uint8'), ('key', 'l2addr')) pyroute2-0.4.21/pyroute2/dhcp/dhcp4msg.py0000664000175000017500000000443413127143031020077 0ustar peetpeet00000000000000from socket import inet_pton from socket import inet_ntop from socket import AF_INET from pyroute2.dhcp import dhcpmsg from pyroute2.dhcp import option class dhcp4msg(dhcpmsg): # # https://www.ietf.org/rfc/rfc2131.txt # fields = (('op', 'uint8', 1), # request ('htype', 'uint8', 1), # ethernet ('hlen', 'uint8', 6), # ethernet addr len ('hops', 'uint8'), ('xid', 'uint32'), ('secs', 'uint16'), ('flags', 'uint16'), ('ciaddr', 'ip4addr'), ('yiaddr', 'ip4addr'), ('siaddr', 'ip4addr'), ('giaddr', 'ip4addr'), ('chaddr', 'l2paddr'), ('sname', '64s'), ('file', '128s'), ('cookie', '4s', b'c\x82Sc')) # # https://www.ietf.org/rfc/rfc2132.txt # options = ((0, 'pad', 'none'), (1, 'subnet_mask', 'ip4addr'), (2, 'time_offset', 'be32'), (3, 'router', 'ip4list'), (4, 'time_server', 'ip4list'), (5, 'ien_name_server', 'ip4list'), (6, 'name_server', 'ip4list'), (7, 'log_server', 'ip4list'), (8, 'cookie_server', 'ip4list'), (9, 'lpr_server', 'ip4list'), (50, 'requested_ip', 'ip4addr'), (51, 'lease_time', 'be32'), (53, 'message_type', 'uint8'), (54, 'server_id', 'ip4addr'), (55, 'parameter_list', 'array8'), (57, 'messagi_size', 'be16'), (58, 'renewal_time', 'be32'), (59, 'rebinding_time', 'be32'), (60, 'vendor_id', 'string'), (61, 'client_id', 'client_id'), (255, 'end', 'none')) class ip4addr(option): policy = {'format': '4s', 'encode': lambda x: inet_pton(AF_INET, x), 'decode': lambda x: inet_ntop(AF_INET, x)} class ip4list(option): policy = {'format': 'string', 'encode': lambda x: ''.join([inet_pton(AF_INET, i) for i in x]), 'decode': lambda x: [inet_ntop(AF_INET, x[i*4:i*4+4]) for i in range(len(x)//4)]} pyroute2-0.4.21/pyroute2/dhcp/dhcp4socket.py0000664000175000017500000001045713123443516020612 0ustar peetpeet00000000000000''' IPv4 DHCP socket ================ ''' from pyroute2.common import AddrPool from pyroute2.protocols import udpmsg from pyroute2.protocols import udp4_pseudo_header from pyroute2.protocols import ethmsg from pyroute2.protocols import ip4msg from pyroute2.protocols.rawsocket import RawSocket from pyroute2.dhcp.dhcp4msg import dhcp4msg def listen_udp_port(port=68): # pre-scripted BPF code that matches UDP port bpf_code = [[40, 0, 0, 12], [21, 0, 8, 2048], [48, 0, 0, 23], [21, 0, 6, 17], [40, 0, 0, 20], [69, 4, 0, 8191], [177, 0, 0, 14], [72, 0, 0, 16], [21, 0, 1, port], [6, 0, 0, 65535], [6, 0, 0, 0]] return bpf_code class DHCP4Socket(RawSocket): ''' Parameters: * ifname -- interface name to work on This raw socket binds to an interface and installs BPF filter to get only its UDP port. It can be used in poll/select and provides also the context manager protocol, so can be used in `with` statements. It does not provide any DHCP state machine, and does not inspect DHCP packets, it is totally up to you. No default values are provided here, except `xid` -- DHCP transaction ID. If `xid` is not provided, DHCP4Socket generates it for outgoing messages. ''' def __init__(self, ifname, port=68): RawSocket.__init__(self, ifname, listen_udp_port(port)) self.port = port # Create xid pool # # Every allocated xid will be released automatically after 1024 # alloc() calls, there is no need to call free(). Minimal xid == 16 self.xid_pool = AddrPool(minaddr=16, release=1024) def __enter__(self): return self def __exit__(self, exc_type, exc_value, traceback): self.close() def put(self, msg=None, dport=67): ''' Put DHCP message. Parameters: * msg -- dhcp4msg instance * dport -- DHCP server port If `msg` is not provided, it is constructed as default BOOTREQUEST + DHCPDISCOVER. Examples:: sock.put(dhcp4msg({'op': BOOTREQUEST, 'chaddr': 'ff:11:22:33:44:55', 'options': {'message_type': DHCPREQUEST, 'parameter_list': [1, 3, 6, 12, 15], 'requested_ip': '172.16.101.2', 'server_id': '172.16.101.1'}})) The method returns dhcp4msg that was sent, so one can get from there `xid` (transaction id) and other details. ''' # DHCP layer dhcp = msg or dhcp4msg({'chaddr': self.l2addr}) # dhcp transaction id if dhcp['xid'] is None: dhcp['xid'] = self.xid_pool.alloc() data = dhcp.encode().buf # UDP layer udp = udpmsg({'sport': self.port, 'dport': dport, 'len': 8 + len(data)}) udph = udp4_pseudo_header({'dst': '255.255.255.255', 'len': 8 + len(data)}) udp['csum'] = self.csum(udph.encode().buf + udp.encode().buf + data) udp.reset() # IPv4 layer ip4 = ip4msg({'len': 20 + 8 + len(data), 'proto': 17, 'dst': '255.255.255.255'}) ip4['csum'] = self.csum(ip4.encode().buf) ip4.reset() # MAC layer eth = ethmsg({'dst': 'ff:ff:ff:ff:ff:ff', 'src': self.l2addr, 'type': 0x800}) data = eth.encode().buf +\ ip4.encode().buf +\ udp.encode().buf +\ data self.send(data) dhcp.reset() return dhcp def get(self): ''' Get the next incoming packet from the socket and try to decode it as IPv4 DHCP. No analysis is done here, only MAC/IPv4/UDP headers are stripped out, and the rest is interpreted as DHCP. ''' (data, addr) = self.recvfrom(4096) eth = ethmsg(buf=data).decode() ip4 = ip4msg(buf=data, offset=eth.offset).decode() udp = udpmsg(buf=data, offset=ip4.offset).decode() return dhcp4msg(buf=data, offset=udp.offset).decode() pyroute2-0.4.21/pyroute2/common.py0000664000175000017500000004015113151505005016734 0ustar peetpeet00000000000000# -*- coding: utf-8 -*- ''' Common utilities ''' import io import re import os import time import sys import errno import types import struct import socket import logging import threading log = logging.getLogger(__name__) try: basestring = basestring reduce = reduce file = file except NameError: basestring = (str, bytes) from functools import reduce reduce = reduce file = io.BytesIO AF_MPLS = 28 AF_PIPE = 255 # Right now AF_MAX == 40 DEFAULT_RCVBUF = 16384 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 View(object): ''' A read-only view of a dictionary object. ''' def __init__(self, src=None, path=None, constraint=lambda k, v: True): self.src = src if src is not None else {} if path is not None: path = path.split('/') for step in path: self.src = getattr(self.src, step) self.constraint = constraint def __getitem__(self, key): if key in self.keys(): return self.src[key] raise KeyError() def __setitem__(self, key, value): raise NotImplementedError() def __delitem__(self, key): raise NotImplementedError() def get(self, key, default=None): try: return self[key] except KeyError: return default def _filter(self): ret = [] for (key, value) in tuple(self.src.items()): try: if self.constraint(key, value): ret.append((key, value)) except Exception as e: log.error("view filter error: %s", e) return ret def keys(self): return [x[0] for x in self._filter()] def values(self): return [x[1] for x in self._filter()] def items(self): return self._filter() def __iter__(self): for key in self.keys(): yield key def __repr__(self): return repr(dict(self._filter())) class Namespace(object): def __init__(self, parent, override=None): self.parent = parent self.override = override or {} def __getattr__(self, key): if key in ('parent', 'override'): return object.__getattr__(self, key) elif key in self.override: return self.override[key] else: ret = getattr(self.parent, key) # ACHTUNG # # if the attribute we got with `getattr` # is a method, rebind it to the Namespace # object, so all subsequent getattrs will # go through the Namespace also. # if isinstance(ret, types.MethodType): ret = type(ret)(ret.__func__, self) return ret def __setattr__(self, key, value): if key in ('parent', 'override'): object.__setattr__(self, key, value) elif key in self.override: self.override[key] = value else: setattr(self.parent, key, value) class Dotkeys(dict): ''' This is a sick-minded hack of dict, intended to be an eye-candy. It allows to get dict's items by 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 elif key[:4] == 'set_': def set_value(value): self[key[4:]] = value return self return set_value elif key in self: return self[key] else: raise e 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, normalize=None): ''' 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'} The `normalize` parameter can be: - None — no name transformation will be done - True — cut the prefix and `lower()` the rest - lambda x: 
 — apply the function to every name ''' nmap = {None: lambda x: x, True: lambda x: x[len(prefix):].lower()} if not isinstance(normalize, types.FunctionType): normalize = nmap[normalize] by_name = dict([(normalize(i), ns[i]) for i in ns.keys() if i.startswith(prefix)]) by_value = dict([(ns[i], normalize(i)) for i in ns.keys() if i.startswith(prefix)]) return (by_name, by_value) def getbroadcast(addr, mask, family=socket.AF_INET): # 1. convert addr to int i = socket.inet_pton(family, addr) if family == socket.AF_INET: i = struct.unpack('>I', i)[0] a = 0xffffffff l = 32 elif family == socket.AF_INET6: i = struct.unpack('>QQ', i) i = i[0] << 64 | i[1] a = 0xffffffffffffffffffffffffffffffff l = 128 else: raise NotImplementedError('family not supported') # 2. calculate mask m = (a << l - mask) & a # 3. calculate default broadcast n = (i & m) | a >> mask # 4. convert it back to the normal address form if family == socket.AF_INET: n = struct.pack('>I', n) else: n = struct.pack('>QQ', n >> 64, n & (a >> 64)) return socket.inet_ntop(family, n) def dqn2int(mask): ''' IPv4 dotted quad notation to int mask conversion ''' return bin(struct.unpack('>L', socket.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) def hexload(data): ret = ''.join(chr(int(x, 16)) for x in data.split(':')) if sys.version[0] == '3': return bytes(ret, 'ascii') else: return bytes(ret) def load_dump(f, meta=None): ''' Load a packet dump from an open file-like object. Supported dump formats: * strace hex dump (\\x00\\x00...) * pyroute2 hex dump (00:00:...) Simple markup is also supported. Any data from # or ; till the end of the string is a comment and ignored. Any data after . till EOF is ignored as well. With #! starts an optional code block. All the data in the code block will be read and returned via metadata dictionary. ''' data = '' code = None for a in f.readlines(): if code is not None: code += a continue offset = 0 length = len(a) while offset < length: if a[offset] in (' ', '\t', '\n'): offset += 1 elif a[offset] == '#': if a[offset:offset+2] == '#!': # read and save the code block; # do not parse it here code = '' break elif a[offset] == '.': return data elif a[offset] == '\\': # strace hex format data += chr(int(a[offset+2:offset+4], 16)) offset += 4 else: # pyroute2 hex format data += chr(int(a[offset:offset+2], 16)) offset += 3 if isinstance(meta, dict): meta['code'] = code if sys.version[0] == '3': return bytes(data, 'iso8859-1') else: return data class AddrPool(object): ''' Address pool ''' cell = 0xffffffffffffffff def __init__(self, minaddr=0xf, maxaddr=0xffffff, reverse=False, release=False): self.cell_size = 0 # in bits mx = self.cell self.reverse = reverse self.release = release self.allocated = 0 if self.release and not isinstance(self.release, int): raise TypeError() 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: if self.release: self.free(ret, ban=self.release) self.allocated += 1 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 locate(self, addr): if self.reverse: addr = self.maxaddr - addr else: addr -= self.minaddr base = addr // self.cell_size bit = addr % self.cell_size try: is_allocated = not self.addr_map[base] & (1 << bit) except IndexError: is_allocated = False return (base, bit, is_allocated) def setaddr(self, addr, value): if value not in ('free', 'allocated'): raise TypeError() with self.lock: base, bit, is_allocated = self.locate(addr) if value == 'free' and is_allocated: self.allocated -= 1 self.addr_map[base] |= 1 << bit elif value == 'allocated' and not is_allocated: self.allocated += 1 self.addr_map[base] &= ~(1 << bit) def free(self, addr, ban=0): with self.lock: if ban != 0: self.ban.append({'addr': addr, 'counter': ban}) else: base, bit, is_allocated = self.locate(addr) 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.allocated -= 1 self.addr_map[base] ^= 1 << bit def _fnv1_python2(data): ''' FNV1 -- 32bit hash, python2 version @param data: input @type data: bytes @return: 32bit int hash @rtype: int See: http://www.isthe.com/chongo/tech/comp/fnv/index.html ''' hval = 0x811c9dc5 for i in range(len(data)): hval *= 0x01000193 hval ^= struct.unpack('B', data[i])[0] return hval & 0xffffffff def _fnv1_python3(data): ''' FNV1 -- 32bit hash, python3 version @param data: input @type data: bytes @return: 32bit int hash @rtype: int See: http://www.isthe.com/chongo/tech/comp/fnv/index.html ''' hval = 0x811c9dc5 for i in range(len(data)): hval *= 0x01000193 hval ^= data[i] return hval & 0xffffffff if sys.version[0] == '3': fnv1 = _fnv1_python3 else: fnv1 = _fnv1_python2 def uuid32(): ''' Return 32bit UUID, based on the current time and pid. @return: 32bit int uuid @rtype: int ''' return fnv1(struct.pack('QQ', int(time.time() * 1000000), os.getpid())) def uifname(): ''' Return a unique interface name based on a prime function @return: interface name @rtype: str ''' return 'pr%x' % uuid32() def map_exception(match, subst): ''' Decorator to map exception types ''' def wrapper(f): def decorated(*argv, **kwarg): try: f(*argv, **kwarg) except Exception as e: if match(e): raise subst(e) raise return decorated return wrapper def map_enoent(f): ''' Shortcut to map OSError(2) -> OSError(95) ''' return map_exception(lambda x: (isinstance(x, OSError) and x.errno == errno.ENOENT), lambda x: OSError(errno.EOPNOTSUPP, 'Operation not supported'))(f) def metaclass(mc): def wrapped(cls): nvars = {} skip = ['__dict__', '__weakref__'] slots = cls.__dict__.get('__slots__') if not isinstance(slots, (list, tuple)): slots = [slots] for k in slots: skip.append(k) for (k, v) in cls.__dict__.items(): if k not in skip: nvars[k] = v return mc(cls.__name__, cls.__bases__, nvars) return wrapped pyroute2-0.4.21/pyroute2/proxy.py0000664000175000017500000000451013123443516016633 0ustar peetpeet00000000000000''' Netlink proxy engine ''' import errno import struct import logging import traceback import threading from pyroute2.netlink.exceptions import NetlinkError log = logging.getLogger(__name__) 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, msg): # # match the packet # ptype = msg['header']['type'] plugin = self.pmap.get(ptype, None) if plugin is not None: with self.lock: try: ret = plugin(msg, self.nl) if ret is None: # # The packet is terminated in the plugin, # return the NLMSG_ERR == 0 # # FIXME: optimize # newmsg = struct.pack('IHH', 40, 2, 0) newmsg += msg.data[8:16] newmsg += struct.pack('I', 0) # nlmsgerr struct alignment newmsg += b'\0' * 20 return {'verdict': self.policy, 'data': newmsg} else: return ret except Exception as e: log.error(''.join(traceback.format_stack())) log.error(traceback.format_exc()) # errmsg if isinstance(e, (OSError, IOError)): code = e.errno elif isinstance(e, NetlinkError): code = e.code else: code = errno.ECOMM newmsg = struct.pack('HH', 2, 0) newmsg += msg.data[8:16] newmsg += struct.pack('I', code) newmsg += msg.data newmsg = struct.pack('I', len(newmsg) + 4) + newmsg return {'verdict': 'error', 'data': newmsg} return None pyroute2-0.4.21/pyroute2/devlink.py0000664000175000017500000000337113123443516017112 0ustar peetpeet00000000000000from pyroute2.netlink import NLM_F_REQUEST from pyroute2.netlink import NLM_F_DUMP from pyroute2.netlink.devlink import DevlinkSocket from pyroute2.netlink.devlink import devlinkcmd from pyroute2.netlink.devlink import DEVLINK_NAMES class DL(DevlinkSocket): 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(DL, self).__init__(*argv, **kwarg) # do automatic bind # FIXME: unfortunately we can not omit it here try: self.bind(groups, async) except: # thanks to jtluka at redhat.com and the LNST # team for the fixed fd leak super(DL, self).close() raise def list(self): return self.get_dump() def get_dump(self): msg = devlinkcmd() msg['cmd'] = DEVLINK_NAMES['DEVLINK_CMD_GET'] return self.nlm_request(msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_DUMP) def port_list(self): return self.get_port_dump() def get_port_dump(self): msg = devlinkcmd() msg['cmd'] = DEVLINK_NAMES['DEVLINK_CMD_PORT_GET'] return self.nlm_request(msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_DUMP) pyroute2-0.4.21/pyroute2/arp.py0000664000175000017500000000501413123443516016234 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.4.21/pyroute2/iwutil.py0000664000175000017500000003773713123443516017010 0ustar peetpeet00000000000000# -*- coding: utf-8 -*- ''' IW module ========= Experimental wireless module — nl80211 support. Disclaimer ---------- Unlike IPRoute, which is mostly usable, though is far from complete yet, the IW module is in the very initial state. Neither the module itself, nor the message class cover the nl80211 functionality reasonably enough. So if you're going to use it, brace yourself — debug is coming. Messages -------- nl80211 messages are defined here:: pyroute2/netlink/nl80211/__init__.py Pls notice NLAs of type `hex`. On the early development stage `hex` allows to inspect incoming data as a hex dump and, occasionally, even make requests with such NLAs. But it's not a production way. The type `hex` in the NLA definitions means that this particular NLA is not handled yet properly. If you want to use some NLA which is defined as `hex` yet, pls find out a specific type, patch the message class and submit your pull request on github. If you're not familiar with NLA types, take a look at RTNL definitions:: pyroute2/netlink/rtnl/ndmsg.py and so on. Communication with the kernel ----------------------------- There are several methods of the communication with the kernel. * `sendto()` — lowest possible, send a raw binary data * `put()` — send a netlink message * `nlm_request()` — send a message, return the response * `get()` — get a netlink message * `recv()` — get a raw binary data from the kernel There are no errors on `put()` usually. Any `permission denied`, any `invalid value` errors are returned from the kernel with netlink also. So if you do `put()`, but don't do `get()`, be prepared to miss errors. The preferred method for the communication is `nlm_request()`. It tracks the message ID, returns the corresponding response. In the case of errors `nlm_request()` raises an exception. To get the response on any operation with nl80211, use flag `NLM_F_ACK`. Reverse it ---------- If you're too lazy to read the kernel sources, but still need something not implemented here, you can use reverse engineering on a reference implementation. E.g.:: # strace -e trace=network -f -x -s 4096 \\ iw phy phy0 interface add test type monitor Will dump all the netlink traffic between the program `iw` and the kernel. Three first packets are the generic netlink protocol discovery, you can ignore them. All that follows, is the nl80211 traffic:: sendmsg(3, {msg_name(12)={sa_family=AF_NETLINK, ... }, msg_iov(1)=[{"\\x30\\x00\\x00\\x00\\x1b\\x00\\x05 ...", 48}], msg_controllen=0, msg_flags=0}, 0) = 48 recvmsg(3, {msg_name(12)={sa_family=AF_NETLINK, ... }, msg_iov(1)=[{"\\x58\\x00\\x00\\x00\\x1b\\x00\\x00 ...", 16384}], msg_controllen=0, msg_flags=0}, 0) = 88 ... With `-s 4096` you will get the full dump. Then copy the strings from `msg_iov` to a file, let's say `data`, and run the decoder:: $ pwd /home/user/Projects/pyroute2 $ export PYTHONPATH=`pwd` $ python scripts/decoder.py pyroute2.netlink.nl80211.nl80211cmd data You will get the session decoded:: {'attrs': [['NL80211_ATTR_WIPHY', 0], ['NL80211_ATTR_IFNAME', 'test'], ['NL80211_ATTR_IFTYPE', 6]], 'cmd': 7, 'header': {'flags': 5, 'length': 48, 'pid': 3292542647, 'sequence_number': 1430426434, 'type': 27}, 'reserved': 0, 'version': 0} {'attrs': [['NL80211_ATTR_IFINDEX', 23811], ['NL80211_ATTR_IFNAME', 'test'], ['NL80211_ATTR_WIPHY', 0], ['NL80211_ATTR_IFTYPE', 6], ['NL80211_ATTR_WDEV', 4], ['NL80211_ATTR_MAC', 'a4:4e:31:43:1c:7c'], ['NL80211_ATTR_GENERATION', '02:00:00:00']], 'cmd': 7, 'header': {'flags': 0, 'length': 88, 'pid': 3292542647, 'sequence_number': 1430426434, 'type': 27}, 'reserved': 0, 'version': 1} Now you know, how to do a request and what you will get as a response. Sample collected data is in the `scripts` directory. Submit changes -------------- Please do not hesitate to submit the changes on github. Without your patches this module will not evolve. ''' from pyroute2.netlink import NLM_F_ACK 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 from pyroute2.netlink.nl80211 import IFTYPE_NAMES from pyroute2.netlink.nl80211 import CHAN_WIDTH from pyroute2.netlink.nl80211 import BSS_STATUS_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 del_interface(self, dev): ''' Delete a virtual interface - dev — device index ''' msg = nl80211cmd() msg['cmd'] = NL80211_NAMES['NL80211_CMD_DEL_INTERFACE'] msg['attrs'] = [['NL80211_ATTR_IFINDEX', dev]] self.nlm_request(msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK) def add_interface(self, ifname, iftype, dev=None, phy=0): ''' Create a virtual interface - ifname — name of the interface to create - iftype — interface type to create - dev — device index - phy — phy index One should specify `dev` (device index) or `phy` (phy index). If no one specified, phy == 0. `iftype` can be integer or string: 1. adhoc 2. station 3. ap 4. ap_vlan 5. wds 6. monitor 7. mesh_point 8. p2p_client 9. p2p_go 10. p2p_device 11. ocb ''' # lookup the interface type iftype = IFTYPE_NAMES.get(iftype, iftype) assert isinstance(iftype, int) msg = nl80211cmd() msg['cmd'] = NL80211_NAMES['NL80211_CMD_NEW_INTERFACE'] msg['attrs'] = [['NL80211_ATTR_IFNAME', ifname], ['NL80211_ATTR_IFTYPE', iftype]] if dev is not None: msg['attrs'].append(['NL80211_ATTR_IFINDEX', dev]) elif phy is not None: msg['attrs'].append(['NL80211_ATTR_WIPHY', phy]) else: raise TypeError('no device specified') self.nlm_request(msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK) def list_dev(self): ''' Get list of all wifi network interfaces ''' return self.get_interfaces_dump() def list_wiphy(self): ''' Get list of all phy devices ''' 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_phy_name(self, attr): return 'phy%i' % attr.get_attr('NL80211_ATTR_WIPHY') def _get_frequency(self, attr): try: return attr.get_attr('NL80211_ATTR_WIPHY_FREQ') except: return 0 def get_interfaces_dict(self): ''' Get interfaces dictionary ''' ret = {} for wif in self.get_interfaces_dump(): chan_width = wif.get_attr('NL80211_ATTR_CHANNEL_WIDTH') freq = self._get_frequency(wif) if chan_width is not None else 0 wifname = wif.get_attr('NL80211_ATTR_IFNAME') ret[wifname] = [wif.get_attr('NL80211_ATTR_IFINDEX'), self._get_phy_name(wif), wif.get_attr('NL80211_ATTR_MAC'), freq, chan_width] return ret def get_interfaces_dump(self): ''' Get interfaces dump ''' msg = nl80211cmd() msg['cmd'] = NL80211_NAMES['NL80211_CMD_GET_INTERFACE'] 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 ( 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 get_stations(self, ifindex): ''' Get stations by ifindex ''' msg = nl80211cmd() msg['cmd'] = NL80211_NAMES['NL80211_CMD_GET_STATION'] msg['attrs'] = [['NL80211_ATTR_IFINDEX', ifindex]] return self.nlm_request(msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_DUMP) def join_ibss(self, ifindex, ssid, freq, bssid=None, channel_fixed=False, width=None, center=None, center2=None): ''' Connect to network by ssid - ifindex - IFINDEX of the interface to perform the connection - ssid - Service set identification - freq - Frequency in MHz - bssid - The MAC address of target interface - channel_fixed: Boolean flag - width - Channel width - center - Central frequency of the 40/80/160 MHz channel - center2 - Center frequency of second segment if 80P80 If the flag of channel_fixed is True, one should specify both the width and center of the channel `width` can be integer of string: 0. 20_noht 1. 20 2. 40 3. 80 4. 80p80 5. 160 6. 5 7. 10 ''' msg = nl80211cmd() msg['cmd'] = NL80211_NAMES['NL80211_CMD_JOIN_IBSS'] msg['attrs'] = [['NL80211_ATTR_IFINDEX', ifindex], ['NL80211_ATTR_SSID', ssid], ['NL80211_ATTR_WIPHY_FREQ', freq]] if channel_fixed: msg['attrs'].append(['NL80211_ATTR_FREQ_FIXED', None]) width = CHAN_WIDTH.get(width, width) assert isinstance(width, int) if width in [2, 3, 5] and center: msg['attrs'].append(['NL80211_ATTR_CHANNEL_WIDTH', width]) msg['attrs'].append(['NL80211_ATTR_CENTER_FREQ1', center]) elif width == 4 and center and center2: msg['attrs'].append(['NL80211_ATTR_CHANNEL_WIDTH', width]) msg['attrs'].append(['NL80211_ATTR_CENTER_FREQ1', center]) msg['attrs'].append(['NL80211_ATTR_CENTER_FREQ2', center2]) elif width in [0, 1, 6, 7]: msg['attrs'].append(['NL80211_ATTR_CHANNEL_WIDTH', width]) else: raise TypeError('No channel specified') if bssid is not None: msg['attrs'].append(['NL80211_ATTR_MAC', bssid]) self.nlm_request(msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK) def leave_ibss(self, ifindex): ''' Leave the IBSS -- the IBSS is determined by the network interface ''' msg = nl80211cmd() msg['cmd'] = NL80211_NAMES['NL80211_CMD_LEAVE_IBSS'] msg['attrs'] = [['NL80211_ATTR_IFINDEX', ifindex]] self.nlm_request(msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK) def connect(self, ifindex, ssid, bssid=None): ''' Connect to the ap with ssid and bssid ''' 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.nlm_request(msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK) def disconnect(self, ifindex): ''' Disconnect the device ''' msg = nl80211cmd() msg['cmd'] = NL80211_NAMES['NL80211_CMD_DISCONNECT'] msg['attrs'] = [['NL80211_ATTR_IFINDEX', ifindex]] self.nlm_request(msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK) def scan(self, ifindex): ''' Trigger scan and get results. Triggering scan usually requires root, and can take a couple of seconds. ''' # Prepare a second netlink socket to get the scan results. # The issue is that the kernel can send the results notification # before we get answer for the NL80211_CMD_TRIGGER_SCAN nsock = NL80211() nsock.bind() nsock.add_membership('scan') # send scan request msg = nl80211cmd() msg['cmd'] = NL80211_NAMES['NL80211_CMD_TRIGGER_SCAN'] msg['attrs'] = [['NL80211_ATTR_IFINDEX', ifindex]] self.nlm_request(msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK) # monitor the results notification on the secondary socket scanResultNotFound = True while scanResultNotFound: listMsg = nsock.get() for msg in listMsg: if msg["event"] == "NL80211_CMD_NEW_SCAN_RESULTS": scanResultNotFound = False break # close the secondary socket nsock.close() # request the results 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) def get_associated_bss(self, ifindex): ''' Returns the same info like scan() does, but only about the currently associated BSS. Unlike scan(), it returns immediately and doesn't require root. ''' # When getting scan results without triggering scan first, # you'll always get the information about currently associated BSS # # However, it may return other BSS, if last scan wasn't very # long time go msg = nl80211cmd() msg['cmd'] = NL80211_NAMES['NL80211_CMD_GET_SCAN'] msg['attrs'] = [['NL80211_ATTR_IFINDEX', ifindex]] res = self.nlm_request(msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_DUMP) for x in res: attr_bss = x.get_attr('NL80211_ATTR_BSS') if attr_bss is not None: status = attr_bss.get_attr('NL80211_BSS_STATUS') if status in (BSS_STATUS_NAMES['associated'], BSS_STATUS_NAMES['ibss_joined']): return x return None pyroute2-0.4.21/README.make.md0000664000175000017500000000561713123443516015513 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: epydoc -------------- Build API documentation. Requires `epydoc`. Pls notice that epydoc is discontinued. The reason to support it here is that it performs additional API testing and helps to highlight API inconsistencies. No issues regarding epydoc output format are accepted. target: test ------------ Run tests against current code. Command line options: * python -- path to the Python to use * nosetests -- path to nosetests to use * wlevel -- the Python -W level * coverage -- set `coverage=html` to get coverage report * pdb -- set `pdb=true` to launch pdb on errors * module -- run only specific test module * skip -- skip tests by pattern To run the full test cycle on the project, using a specific python, making html coverage report:: $ sudo make test python=python3 coverage=html To run a specific test module:: $ sudo make test module=general:test_ipdb.py:TestExplicit The module parameter syntax:: ## module=package[:test_file.py[:TestClass[.test_case]]] $ sudo make test module=lnst $ sudo make test module=general:test_ipr.py $ sudo make test module=general:test_ipdb.py:TestExplicit There are several test packages: * general -- common functional tests * eventlet -- Neutron compatibility tests * lnst -- LNST compatibility tests For each package a new Python instance is launched, keep that in mind since it affects the code coverage collection. It is possible to skip tests by a pattern:: $ sudo make test skip=test_stress target: test-ci --------------- Run tests on isolated VMs defined by `tests/ci/configs/*xml`. Requires qemu, kvm, libvirt and civm script: https://github.com/svinota/civm Command line options: * civm -- path to the civm script (if it is not in `$PATH`) target: dist ------------ Make Python distribution package. Command line options: * python -- the Python to use target: install --------------- Build 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 target: develop --------------- Build the package and deploy the egg-link with setuptools. No code will be deployed into the system directories, but instead the local package directory will be visible to the python. In that case one can change the code locally and immediately test it system-wide without running `make install`. * python -- the Python to use other targets ------------- Other targets are either utility targets to be used internally, or hooks for related projects. You can safely ignore them. pyroute2-0.4.21/README.license.md0000664000175000017500000000051213123443516016205 0ustar peetpeet00000000000000Pyroute2 package is dual licensed since 0.3.6, emerging two licenses: * GPL v2+ * Apache v2 It means, that being writing some derived code, or including the library into distribution, you are free to choose the license from the list above. Apache v2 license was included to make the code compatible with the OpenStack project. pyroute2-0.4.21/docs/0000775000175000017500000000000013152333366014242 5ustar peetpeet00000000000000pyroute2-0.4.21/docs/html/0000775000175000017500000000000013152333366015206 5ustar peetpeet00000000000000pyroute2-0.4.21/docs/html/netns.html0000664000175000017500000007256113152333365017235 0ustar peetpeet00000000000000 Netns management overview — pyroute2 0.4.21 documentation

Netns management overview¶

Pyroute2 provides basic namespaces management support. Here’s a quick overview of typical netns tasks and related pyroute2 tools.

Move an interface to a namespace¶

Though this task is managed not via netns module, it should be mentioned here as well. To move an interface to a netns, one should provide IFLA_NET_NS_FD nla in a set link RTNL request. The nla is an open FD number, that refers to already created netns. The pyroute2 library provides also a possibility to specify not a FD number, but a netns name as a string. In that case the library will try to lookup the corresponding netns in the standard location.

Create veth and move the peer to a netns with IPRoute:

from pyroute2 import IPRoute
ipr = IPRoute()
ipr.link('add', ifname='v0p0', kind='veth', peer='v0p1')
idx = ipr.link_lookup(ifname='v0p1')[0]
ipr.link('set', index=idx, net_ns_fd='netns_name')

Create veth and move the peer to a netns with IPDB:

from pyroute2 import IPDB
ipdb = IPDB()
ipdb.create(ifname='v0p0', kind='veth', peer='v0p1').commit()
with ipdb.interfaces.v0p1 as i:
    i.net_ns_fd = 'netns_name'

Manage interfaces within a netns¶

This task can be done with NetNS objects. A NetNS object spawns a child and runs it within a netns, providing the same API as IPRoute does:

from pyroute2 import NetNS
ns = NetNS('netns_name')
# do some stuff within the netns
ns.close()

One can even start IPDB on the top of NetNS:

from pyroute2 import NetNS
from pyroute2 import IPDB
ipdb = IPDB(nl=NetNS('netns_name'))
# do some stuff within the netns
ipdb.release()

Spawn a process within a netns¶

For that purpose one can use NSPopen API. It works just as normal Popen, but starts a process within a netns.

List, set, create and remove netns¶

These functions are described below. To use them, import netns module:

from pyroute2 import netns
netns.listnetns()

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.

pyroute2.netns.create(netns, libc=None)¶

Create a network namespace.

pyroute2.netns.listnetns(nspath=None)¶

List available network namespaces.

pyroute2.netns.remove(netns, libc=None)¶

Remove a network namespace.

pyroute2.netns.setns(netns, flags=64, libc=None)¶

Set netns for the current process.

The flags semantics is the same as for the open(2) call:

  • O_CREAT – create netns, if doesn’t exist
  • O_CREAT | O_EXCL – create only if doesn’t exist

NetNS¶

A NetNS object is IPRoute-like. It runs in the main network namespace, but also creates a proxy process running in the required netns. All the netlink requests are done via that proxy process.

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.

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')

To remove a network namespace:

from pyroute2 import NetNS
netns = NetNS('test')
netns.close()
netns.remove()

One should stop it first with close(), and only after that run remove().

class pyroute2.netns.nslink.NetNS(netns, flags=64)¶

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.

remove()¶

Try to remove this network namespace from the system.

pyroute2.netns.nslink.NetNServer(netns, cmdch, brdch, flags=64)¶

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.

NSPopen¶

The NSPopen class has nothing to do with netlink at all, but it is required to have a reasonable network namespace support.

class pyroute2.netns.process.proxy.NSPopen(nsname, *argv, **kwarg)¶

A proxy class to run Popen() object in some network namespace.

Sample to run ip ad command in nsname network namespace:

nsp = NSPopen('nsname', ['ip', 'ad'], stdout=subprocess.PIPE)
print(nsp.communicate())
nsp.wait()
nsp.release()

The NSPopen class was intended to be a drop-in replacement for the Popen class, but there are still some important differences.

The NSPopen object implicitly spawns a child python process to be run in the background in a network namespace. The target process specified as the argument of the NSPopen will be started in its turn from this child. Thus all the fd numbers of the running NSPopen object are meaningless in the context of the main process. Trying to operate on them, one will get ‘Bad file descriptor’ in the best case or a system call working on a wrong file descriptor in the worst case. A possible solution would be to transfer file descriptors between the NSPopen object and the main process, but it is not implemented yet.

The process’ diagram for NSPopen(‘test’, [‘ip’, ‘ad’]):

+---------------------+     +--------------+     +------------+
| main python process |<--->| child python |<--->| netns test |
| NSPopen()           |     | Popen()      |     | $ ip ad    |
+---------------------+     +--------------+     +------------+

As a workaround for the issue with file descriptors, some additional methods are available on file objects stdin, stdout and stderr. E.g., one can run fcntl calls:

from fcntl import F_GETFL
from pyroute2 import NSPopen
from subprocess import PIPE

proc = NSPopen('test', ['my_program'], stdout=PIPE)
flags = proc.stdout.fcntl(F_GETFL)

In that way one can use fcntl(), ioctl(), flock() and lockf() calls.

Another additional method is release(), which can be used to explicitly stop the proxy process and release all the resources.

release()¶

Explicitly stop the proxy process and release all the resources. The NSPopen object can not be used after the release() call.

pyroute2-0.4.21/docs/html/debug.html0000664000175000017500000003335413152333365017171 0ustar peetpeet00000000000000 Netlink debug howto — pyroute2 0.4.21 documentation
pyroute2-0.4.21/docs/html/report.html0000664000175000017500000001333513152333365017413 0ustar peetpeet00000000000000 report a bug — pyroute2 0.4.21 documentation

report a bug¶

In the case you have issues, please report them to the project bug tracker: https://github.com/svinota/pyroute2/issues

It is important to provide all the required information with your report:

  • Linux kernel version
  • Python version
  • Specific environment, if used – gevent, eventlet etc.

Sometimes it is needed to measure specific system parameters. There is a code to do that, e.g.:

$ sudo make test-platform

Please keep in mind, that this command will try to create and delete different interface types.

It is possible also to run the test in your code:

from pprint import pprint
from pyroute2.config.test_platform import TestCapsRtnl
pprint(TestCapsRtnl().collect())
pyroute2-0.4.21/docs/html/sm-interfaces.html0000664000175000017500000001643213152333365020641 0ustar peetpeet00000000000000 <no title> — pyroute2 0.4.21 documentation

??]?n???<?n????u??Eg??E[L??? ??h#K?D’?O?듔????lQT,3??X>?)?|? ??O??-“큀Y??7C??F?i???f?Û¯ ?ß?~ B??;O???>g??????????G?3?&)???????(?????@??????6W}J?p??D???N???<MVq0,?6t?$JR???n?f???z3?ui?c/?9??Ä¿????~<???K?V?],?,d$t??”?3?? ͆*cD??????[?x k׍?eB~:?m>ln?b”pBl2,?? ?t???n?-??[vi????v??4?C??r?$??+MW?9?l?Gl? ?Z?bR????̏?c?@q?e???s?i Þž%?=?<???}?4]??kI???Y8?È®?c????v??>?z?~p5?텇?%?N???* HV???4{F?ې]??z??؉).U?:g?k?n??? ???o$??Oi????͜$??ɔn??o???6ÒŸQ??O?( L????W?<S???FbBpk????z?u??Í·?ÆŸ?’???)?q??G??????14?a???[?kxNi??ID??$????6?*>M???,?L???H8????F?6J??3?s???U??,ʪ?a4c1 ??e??4?<Ѻ?T????rY???S?(???QJH?}Ӕ?t_~]???0Z????l?et?E??G?B?Bé?V2?~[knǺ3?[^w?)ÆŽ,?v?QW???Ybl??v?*oˆi??xAv?K?;@???B??IL??-G?$?n??E?s??$Hds O!m?Gq?@?f0 ?aI?i??Qߑ)?VP}Tg댒????)?VX?^X?k???s}l?l$ ?nn?KDm?Ü€6?? , ”????IJ?n??9q?v??A? $?]?4,K?CR??q?=(pO??&i?.Vo뀳?T0??Rt???W????ؚ4?,,?+? ???????Qp?7.?Ȃv???c9y”??? N?TnØIzJ??;?r?r??4??b?lk?>????q?Q?h??l??w?$?;> 7?????m?u冎??/ ?w;?W,??NsY{ ??C??? {?p ?????_+??? c?”?V?wa??????????r?{ ??[??[????q?? M?& ??+?7???4kl.??*sj)e?)?M?mv[yJ3ڠՔ͙Yl? ?b???,?}???(荠ܰ 6>?d??G?lC??{l??? u? ?,?cAuy?g?L??Q ?Vq@fl?u)? r??o??? R???y??%?? v???e? ???=?d??Wd?ǒ1?F?Ѫ Se???0 a?BW]@R??W^?3?8?D?˟?[1Q?q?qpl????? ???f??TX?5?a?-??C&Ì¡u?6?_??/.?}?W??A?????U?D?Di=^??`tg?<ä·Ž_??-?? %OT(AC??B???L???T”OS???b?ݖC?/I?0??Ci?h6S?H?0?F?ȓ,??B?????4z?C&#?NI?dj-?fi?П~SJRr?SJR??p>?Ȓ???wr?????G??A.?ɋDÒµy?@??R)L Yo?%???LG?????!=???<??{????d ??CF]D??|; {?<??L???uÄ¢?X?<? =?t;?CP??y????{I=^??y[?U??????sꅆ? ? ?=9d??-$?aG????c?#????~G,? 4???:??O? WR??Ò¥y0 ????+m8???Db???>?J??Vru ??+1F?O?L?b sp;?????Ig??hA???%??l????w??l???E?@?4?N?[?????9?l?F?T))?&??e?LI?)?]~Ty?+?L???J?N???Z?ك????AK?W????@?b{?V?*=?} ?3?$]?l?????H*d#?1;G??z???Lf?a[`Ύn,???x????0?????? ?r??? ?Ib?:q???g?1g s???[?k?P?9p,?YGx?O ?j?IU.?)???M??<????#????}?? ? ??? {??5?Nwлm?Ú«???a?J0D?YU???8q.? ??v?,S?U?o??{??i?ۆx??n2=Gnˍ???ذ=?s?wϬ?!4??,?kJ? Ge)5W??J?m????b,”?m??7?Ɣ5?}?/B?e?b?& ?????a??6?6?[1?8?E??? ???ce?? ,`Ց?-ZO J??>??P???!b”GvFb??{???N?e????ΰ??$? ???9Ç¿O?7??????9??E??P?????@?????_X] 4V?

?Q??}=?uT]?l??ySތ?U?}?^???v?*?|SOR??P@??P?????k

? ?z????

8E?.:?6???Ú§b??>??o??t?T?T???SQ ?SQ??’GTCy?^P

?=o?Z??? ?S???wR??^??J?(??????IU?0r䟷pC?r?Þ¡?*??O?M?%?,?V1m? ????n??I?O?{?AÊ????* ??%>?????w????ri??U?s????????BW??vEajÛŠ+??s????2B7???7~??x????h?2?aN?; ”?W?”^!??o&Q?^????`s?L?[,B??%Éœ*??EG?T????kB????Б??7?? ??o?Q????]?I`?TÍŠ?fS?L?I0?*j7??Mҁ ! ??Mʈy??M???<?-????` !????^M?s&+j N2U]tu4??D]uR ^p=?®??’? ????k6p?*???Îs?B?.?P???? R?†?]@2i ?oʏ .Y?<E”?????GIX??ov ?G?? ???? h?}??Ӕ,??Òµ4?????W??;:?)/Ii./??Z?7Fm&O?`r ??0E? &????m?QP?j??7??B8yê°¹??=1O ??59E?K~?̓BL??I!Ø­I?p?>L???Q)!?m??)!&? !?D??>???e? ?? ‘ CK?L?????Xc?t ul?㜿b?a?A+Bc4_ V???UO|??’?F? ???\Ϟ???W????3j u?>??Cˇ~|Ky.?P??k??<\?d?? ?*_,?S?>v ³H?$?p?m3??\”dB\????{?c?!???? ?Cy?H5?F?)??E?)?<?.[R?’oX\”荙?tM??M`??0?!? w?’t#J??2T??g???????}????w? S???$?Q~,$0(\}|??J??ۋ_I?>JA[<??4eL?&.????35,?????G???o?`??”Aq?rwVa?jA?,?$P?^?r?????]eV?:??$?<(^????_| ?Q??r??

pyroute2-0.4.21/docs/html/_images/0000775000175000017500000000000013152333366016612 5ustar peetpeet00000000000000pyroute2-0.4.21/docs/html/_images/inheritance-f66e473c49f615627cbf040680338f04db639d10.png.map0000664000175000017500000000350013152333364027526 0ustar peetpeet00000000000000 pyroute2-0.4.21/docs/html/_images/inheritance-e3c5704fb116d5e1ca6c2d8505ff63c892fa04f2.png0000664000175000017500000012473013152333364027330 0ustar peetpeet00000000000000‰PNG  IHDRôª7ŸpbKGDÿÿÿ œ§“ IDATxœìÝyoÞ<¬SpBuuµ¥¥%ÖAØËÁÁ¡«« ë¬÷àÁƒwïÞa3d29<<\NNë ,6NÎJ€-(nÆ#]]ݔ”¬Sp‰DÂ:'tww‰D¬S°žµµ5Ö°ÔÝÝ͗EÀ89+¶à¶ø 7à+P܀¯@qF‡ÃqìU€ Á¡äZòòòL û÷ï«««ýhÂçp'(n€‡1ʋ   „„tFoÖþþþ0ýàWPÜc(EEE+V¬ŽòkŠŠŠŒŒŒF±;žTx7ÀZZZ&MšÄdãÖÖÖɓ'³5‚â ‡Ã%%%aaa}}ýâââA JKK7lØ !!!&&faaÑÐÐ0ücbbTTT$%%ÝÝÝ©Têð¹u떞žž––Ö•+WЕ4-,,LIII\\ÜÙÙ¹££ƒ¥ïü–› —Rÿ20ŸûåqØXJJª©©‰±µÏŸ?OŸ>œ©©iÈÓmà /þs& ž_•–––]__ohhž}ûöAßµŽŽÜ¹sguuuUUÕ̙3‡ßZzzú;wž?^RR>üFìíí÷îÝûùóçŽŽŽ¬¬,tå©S§nߟ——÷öíÛÞÞÞàà`–Ÿ]Þpîܹˆˆˆúúz싵0ŒìììÀÀÀªªªQoœEÿ¿õåqØxÆ ñññŒÆñññvvvÓŠMòtøÂa¶öè`ü±²²úfAjkkÑ厎<ÏXÿe㖖99¹á·F¡PÐåââb•á7"//ýîÝ» 444^¿~.×ÕÕ)**~ó]‰D"‘øÍf<ÄØØX@@@ZZZOOï×_mii¡3w@Gе€ÑaÇûâN‚HIIéè脇‡×××әþô"ª!W_œz¥  ÐÛÛK§Ó{{{•””*++é_?Ý}Ÿ¶ýaðßY žL¿Ÿjƌ肐Ð—S=~ü8 àéÓ§è5í & ¿5eeetAEEåÇÃo$##ãСCŠŠ244D€ªªJCCƒ±AŠ®;>zôˆ™fŒ‚J¥ÒhŽúúúúúzÐÐPEEEAAAv싵€Qèîî'CõWVV"òéÓ§OŸ>íÝ»÷äɓrrr˖-[Ÿ|ùØ7>üqÔÐÐ ×®]³¶¶NMMÕ××WTTDFrº ¿}0Å %›àà`‰$**ÚÚÚ*..>|ûŠŠ MMMAÊËËgΜ9üFôôôÒÓÓétzNN޳³smm-‚ ¹¹¹³fÍbã»Lép-ookkëššš˜˜tå×N7xZŠ“6oÞÜÝݍu ÖÓ××÷ññaë. ž£ÔÑÑ1uêTaa᪪*ÿo¶÷ññ9þ<º°qãÆá7bffvàÀŽbügêææ¶mÛ¶èèhEEE …–™™ùÍý.\žŸŠLJJ”””““sqq±µµ:u*&LŽô0 SŠLá§c7ŒÂÂBA€€€f̘agg·eË)))Ž]µZµj•¯¯ïéÓ§EDDtuuѕ_;Ý$%%_œz…ž›€Ýzzz`NÜсâŒÒùóç}}}­¬¬dddvïÞ}íÚµáۛ˜˜,_ŸŒ¥¥ÅÚÚ:((hø888899•––jhh$''£+=<<,,,Þœ{§¥¥µÿ~öœ;®ellŒlÙ2'''iiil“Œô†±|ùr·}ûvô–çy{{»ººþñÇŒ5_;ÝöìÙ³téÒææf: ž> ãµµuJJ Ö)8ýۗïÿúç×ʯï‹Iüúéå×÷Åüz pà}Á£à|®¯¯ëGÁm)>7cÆ IIIQQQðý÷ßϝ;WKK‹}»²³!\ÀIPÜð9tÌЇ^ºtILL¬­­MGG‡M»ƒ:žÇçR žáC­­­/_Ÿ,..ŠP(çšÁãñRRRçΝã˛ž ŠžG¥RߌyóòåK …RVVF¥R444æÏŸÿÓO?Mš4éɓ''N”••õôôôööž0a7ÀßøþÚÌð žá1œœœ¥¥¥h)óæÍ›ÞÞ^Ž”!–––šššƒ]Ÿ|ù¹s眝CCC…„„°Š p 7\­¯¯ïÝ»w åå˗¥¥¥mmm4MUUUKKËÒÒRCCã›cÞ¯^œú͛7œ …¿ÿþ UDDDPPpÒ€IÂÂÂïß¿———Ç:à}}}¡¡¡—.]¢R©û÷ïß±cÇÀ¹âÑë78îܹsüðáFcŒ‡ÃÅÄÄÄÄÄŒ}ûVYYù×_-++;|øðû÷ïç͛wñâEtŽ[·nùùùœ~ýzÖ¬Yûöí³³³C€§§ÇÛۛH$Љ‰íÚµkçΝÜs­ŠîRSSSXXøòå˒’’öövA”••µŽŽ~þùg//¯)SЌtƒ‚‚‚PÙð}}ý?2þçlooïïïÇ:“ÄÄÄÄÄDA&Ož>žB¡|øðAIIImmm---333̧k< ­iž>}ª««kmmÍši<:q"œŒŒ¡±±1;;;;;»¿¿ßÄÄääɓ¢¢¢_6[Œxñâŋ9ð??¿ŽŽŽ~ø¡¿¿?88]ÉÚùÛœœœJKK544’““Ñ•ûöíóòòÒÖÖóððÈËËûŽXep‡b~ƒ”µH$Ò͛7ׯ_O fΜ‰uÀóh4Úœ{÷²²²^Ÿ|©§§gccƒ^éŒëýû÷üñÇÝ»w'NœžfÍCCCNö«ü‡~#™™™1ys ƒÅcÁùñ1ñçŸ^µj&»|­iH$Ò«W¯–.]êää4{öl¬C1)//'“É·oß]¿~ý¹sçNŸñññ èééñõõ533Ã:ÎÁ•m8QÓŒ|ùrÙ²eîîîjjjX‡c‚ÞL,..ž9sŠ¥¥¥‡‡tãÁ¬Y³.\H¥RMMMðŽó_PÜÀ!ýýý÷ïß'‘H ÅÀÀÀÃÙ§7×bÜL|ýúµ††ÆºuëŸì óòòòòòÂ:Å ž€œ5MqqñòåË===UTT°F= ×®]+++›?>ÜL€ æÂ)‡KJJ"ÂÂÂúúúÅÅÅ_6ˆÕÑÑÒÖÖ.((HLLTWWÇãñúúú¯_¿F›Ýºu ‡WKKëʕ+èʞž777 •˜˜˜o>—£¢¢"))éîîN¥Rѕ¥¥¥6l³°°hhhf4-,,LIII\\ÜÙÙ¹££c?éïï/((ðññ±²²*,,ô÷÷¿uëVHHT6<ª««‹L&;;;›ššîÞœ›L&‡„„@eå]ᎎŽìììúúzCCÃíÛ·ٝ«âÓ§OvvvFFFééé¹¹¹ ÆÆÆŒöööö{÷îýüùsZZZVVº’1WE~~~ffæ7“€§§ß¹sçùóç%%%áááèJKK˝;wVWWWUU͜9300p˜=ž:uêöíÛyyyoߟíííe~üžxñâÊÊÊá$ AKKËᛁñ£§§'???''§¢¢âÇ޲²8>à9Ÿ>}úã?rrrz{{MMMÍÍÍ¿ûî;¬CŸE£Ñªªª:;;»ººš››;;;;;;ëêêþüóϜœ¬Ó±÷> >äÜql® Ft<=A?~ðôéÓŠŠ&A3ï ¹ÇªªªcŠÀ3€y===þùgZZÚ§OŸV®\éïïÏøš^TYY™™™ù×_‰‰‰­_¿þòåËð 7à€òòò…  ö÷÷wwwS©Ôþþþ3fÈÊÊ¢Kƒ‘²C1K檚šš@„.//g\(¶±± &‘H¢¢¢­­­âââÃìQAA!77wÖ¬Yìy—€uwwßžq#--­ŸŸ~ÕªUáááPÓð4 …’••UTT$%%µnݺ;wÂЀ“TUUåääXçΝ{ãÆû÷ïwwwc›}EØË˜%sUøøøœ?]Øžq#º²££cêÔ©ÂÂÂUUUþþþÃïÑÍÍmÛ¶mÑÑъŠŠ %,,Œ™Ÿ>`BkÆÜ@GށYÃxF{úô)™L~øð¡ššš¥¥¥¿¿ÿXf`túúú®]»Š¢¢òæÍ›žžaaáe˖effN™2ÅØØët<‹þ¿¬¬¬èß2èUŒ/¿\þËŽŽŽ¹sçâñx]]ÝŒŒuêT¬CQjllÌÎÎÎÎÎîïï711£ 0G¥Rÿýw"‘žlÙ²S§NIHH`ˆÏqcqóå\\XÇTUUµµµÁÐŒ®¹¹ùúõë7oÞ€R©hMǔwœ{÷.==ýÞœ{"""kÖ¬¹pႰ°0Ö¡Àx×ÓÓsá…¬¬,[[ÛŽŽ4\€3ž±žá /^ŒðôôlkkÃáp3gÎÔÔÔÔÐÐÐÒ҂n<¡©©‰L&geeÑh4“³gÏBMûÊËËÉdòƒDEE×®]»cÇAAA¬C€ôôô\ºt)==ÝÖÖöúõëŒ×@q3JëÖ­ct(njj¢P(/_ŸLKKCÇÎÁãñZZZÊÊÊZZZššš00 —@kšìì쟟>SSÓ .ˆˆˆ` Œ…B!‘H=RUUµŽŽôôô„¹—èé鉍œyóæ¶mÛrrrà“ÉyPܰÀŽiÓ kš››ßŸ}K¡P’’’ª««©Tª€€€††@PVV&SŠLÁ0ðxóùó第¬5 Ü­àQèŒÜYYY¯^œÒÕÕ566 Á:ÿE£Ñ®]»–˜˜èèèH&“¡¬Á W? εFñ(8•J}óæÍ˗/ËËËKKKÛÚÚQVVF¯î̛7o,—ž={vøðáQ¿œ¿UVV~úôiæÌ™Ó§Oÿæea}}}Îc•Í›7óåŠ ŸŸŸºººèÔ]¥¥¥ ,°³³SWWÇ:Ûññyíëë»dɬS°Þ͛7O:ebbâìì ƒ\ckðOÿýû÷0“Å7=xð`€gæ€I“ÂÀÿúúúÞœ{G¡PîÞœ›””ÔÔÔÔÛÛ+--­¥¥E tttŸ6ú­²²²Íþýûñx<ºæÍ›7–––0îÎØY[[caÄzzzˆD"Ö)؅D"8q‚N§¯\¹2 `\ ͯç5‰Dzÿþ=Ÿ7Ož<9xðà?þ˜šš æ¹Áàâ&00¿ÿ d‰%K–¬X±bŒ™8q"zåfàÛ555èÕ2™\[[K§ÓÅÅÅÑ«;AIIéóçÏT*õرcÉÉɞžžÞÞÞÐI ð·õë×óbÑ Æ‰ÆÆÆ°°°æææ„„x „{ .n`& lÉÊÊÊÊÊ\S__ÿòåËׯ_ßžq£¶¶¶¡¡¡­­ œêsàÀsçÎíß¿ž-| úãîÔ××}÷î݃jkkcüž)È퀥¥¥¥¥üñGôËS§NÝŸ}]îèè())Ùºu«’’ÒÁƒ±JãÍóçÏ÷ïß¿aÆk×®a ŠóèÑ# ‘©S§ÊÉÉ-]ºtÙ²e•••Xç€q¡³³388ž³³óòåËbbbXÇCƒëœ<&44ôþýûåååEEE999ûöí[±bÅk‡Ǐ‚‚yyy&øýû÷ÕÕÕÑÆð!á~£>ž€y÷ïß_¿~œ©©iLL T6Ü Š£¢¢¢ŠŠœÀˆ0~‡%$$ @|ó›¿¿DDN~‚ƒËœœœ!!!çΝ#‘H˗/Ç:øø žüÉ&ŒßaEEEŒýŸù‹­ššÈÈȈœÉÀ˜ÁÁ倲²233³%K–À°æŒúÜ0ŽŽŽŽ0ÿl]kkëäɓٚ°\6IOOOJJJHH‘‘Á: `\¹Gp8\RR@Ö××/..þ²All¬ŽŽŽ¶¶vAAAbb¢ºº:×××ýú5ÚìÖ­[zzzBBBZZZW®\AWöôôž¹¹IHHšššÄÄÄ  ŠŽŽtÆ bbb Èÿ_¶ÁápŒ×öôôx{{ËÈÈ̘1ÃÛÛ»§§gì9ǧýfp0ŸûåãË㢢"))éîîN¥RѕCJä+?|Š€€$..îììÜÑÑÁ¹wkjjzþüùè^;öƒË±ŸçôõõùúúŸxñ"55*ÞÅÍø’–––]__ohhž}ûö/dgggdd|úôÉÎÎÎÈÈ(===77·¡¡ÁØØ˜ÑÞÞÞ~ïÞœŸ?NKKËÊÊBW†……UUUœxñ"???33sø–––;w®®ªªš9sf`` òÿÒét:ãŠú/¿üB¡P=zôøñ㢢¢Cя:çx6ð'<è¶Å—Œ/Gzzú;wž?^RRŽ®òP"_ùá£äååœ}û¶··788˜#ï›äçç/_Ÿ\]]ÝÛÛ»¬¬l[ãÁå̉Ï[Z[[íííøá‡àà`èæÈ{è€÷‰D"‘øÍf‚ £ÓéôŽŽ<ÏXÏXš©©a4øZ{yyùèèèwïÞ Üž²²ò«W¯Ðe …ÂüG«¥¥ENNnPÆ6) ºüâÅ •±çž••ó¹3™â!WÿÁ@—Ç¢žž˜q,x(‡üákhhŒ~ý]®««STTüfr&?ÛŒ.33“1‘€€€ªªêÞœ{ãââ˜<¯-ÐG~p9yâóÄ1}óæÍÚµk‹‹‹±Fiðę€19‘'÷?‡›ñå— Ã·òäÉ¡C‡òóóÅÅÅ£¢¢ ÁãñÍÍÍè]üîînô?į%yüøq@@ÀÓ§O›šš™0aB__ߗ{ÄãñMMMèD-ÝÝÝÓŠMëêêcÎáñâıÌdòóžjÜÕÕõå±øÚ¡ò‡/$$„Ÿ % Ðßß?|r‰”ššÊ÷ÿM©šš?~ýÑ!‚Ãᄄ„äää:Äüy=–ƒË™5Љ‡9ìÏ?ÿ<}úôùó祥¥YŸñgϞœyó†å›åcúúúrrr#}t(#Ч§—žžN§Ósrrœkkk‘••­ššÐÐÐ@€ŒŒ|ø-ØØØ“H$QQÑÖÖVqqqtý ö²²²åååZZZ‚”••Íœ9sì9ÁšUTThjj"R^^Î8_;”Cþðrssg͚5¢ýnذ›²Äõë×Ñ)))111###OOÏÂÂBlS 2öŸ'DGG—””€¥¥ ²cûááá6l`ǖù҃Q•ÂP܀333;pàú{ŽQŽØÚÚúúú&$$ âëë;ü:::ŠN*,,\UUåïïÏX/))ùêÕ+tËè6œŒŒ.^Œˆ ˆ———­­íØs‚Qóññ9þ<º°qãFtå×å?|77·mÛ¶EGG+**R(”°°0>ë¥1jx<~úôéfffîîîh¡€ ·7c?ñ¹Nß³gŽŽôÙ³gÙº#Ÿ/Ö¹»ï{`ŸÏ͐_~¹0ü—iiisçÎÅãñºººyyyèÊ®®.—iÓŠ)))8qBPPp˜$ÙÙÙ³gÏTPPˆŽŽfl922ô“±MéÓ§OŸ>ÝÃã««kì9‡7nûÜ0ÓøìÙ³èƒNnnnÝÝÝèú¯Ê!øýýýÑÑÑBBB ,ÈÌÌüfržèŸÁ&#=¯Çrp‡l3è˱Ÿø#z_ÖÛÛëêêzþüyvïˆÿ“ÁК?-Ðç†pÛ=좢"333^ŒFͯ}nx·}¶9‰çÞ;“'>Ÿ¯ŽŽ'''GGG lÈß',ˍúӏ·–ñññ©«««ªªòõõ533Ã:€xýÄohh°¶¶Þœ{7 ÙÌO Ï `™Y³f-\žJ¥ššš†……!_éé à'_žø<€ªªÊÕÕ5**jöìÙXgù¶A²ñ4v¿(nËxyyyyy \Ã7ç!àkŸ<ñyÅ˗/ÑÞУxÒp9ž-`ÜùçŸüýý“““y±²)//766ž6mÚŽiӌýœètz@@€žžžžžx`` ãÏË¿þúk͚5¢¢¢²²²[·nmlldljÐ|ß\Ï+ ž®ÓÓÓso±OVV։'H$’„„ÖYFcÓŠMsçÎ­šššššÐÑÑÙŽiº>>>þæÍ›………………þù':v‚ رcÇÛ·oŸ={6qâDÆŠÐg‹ŸÜÅ×Öó (nc§OŸÞ°aòeËtuuµŽŽÐ JÙ=ÚÊžG&““““ñx<ÖY†öÍIŽŸ? &&&&&¶gÏƬ«—.]BçŠURR KLLD×ß¹sÇÄÄDRRRZZúرcùùù£ Ö×׬šš(##ÃHËÌÄ«T*uçΝªªª±±±£ À<(ncrÁ IDAT=JKK»wïÞ³gÏ^œzUSS³xñâ]»va‹ßô÷÷ÖÕÕÅÅÅ1&óâNÃOrlddtüøñ–––æææcǎ1žó¢P( .D—,X€Îö5H^^ÞܹsG—*"""???//ïéÓ§K.f&^=|øpiiiQQÑí۷ьيº^g$éÁƒK–,Á:ÏKMMå¹!(–.]êããƒu vA?Ø\5& ;”——  ÿý÷ׯ_OOOçËó«cÚÞÞŸeË33³‘ŽuÎZLNW[[;cÆ A:;;%%%;;;‘O}øðaÙ²eUUU‚(**Þ»wOVVA &tww£GôööâñxƄeš§OŸš˜˜Éäyóæ ÚãÅÀ õjjjéééÚÚڃÚÔÔÔÈÈÈ i………‡ ¯ªªzýúut:ââbfÊçf\[±büXZZú÷ßc›!00Û£À‹™™·dɒ+V`‚úúú.\ž°sçNEEEA„…… ³²²&NœÈçõ—09Š>|°±±Ùœ{7¶• óÐâùbºY”£££]SSSSS“£ˆˆHKK ºÜÜÜüÝwß |ÕíÛ·MLL’““U6Ì«®®VUUýr=ZÙ i¿þÇÊÊÊèòa±±Œ‹ Ñh4[[Û§OŸbN ÑhD"qåʕqqqœœœ………¢¢¢ŽŽŽ4 ëhüæîÝ»FFFÕÕÕX¡ÓG8_Ê / x<Ÿµµ]niiB—õõõsrrÐ圜œe˖16òûï¿ËÊÊþóÏ?ÌìñkëÕÔÔ^ŒxÁdÚA_ªššŒ|ù]Foi ¹ÇAF=ý\¹Ü‡ÃÅÄÄìÙ³§µµë,°×ƒÖ­[W__Ÿ••åââ2qâD==œÔÔԋ/òî÷\ˆF£9r$11‘H$2вçóæÍ;zôhssssssDD„®®.º~óæÍûöíCŸ¢Ú·oŸ££#ºþĉþþþ7oÞ\ŽhÑXöëèèèááQ^^^WW·cǎœÖÎÎÎÇÇçÇ>|ðööK Ќ¢ €}>|hoou Ø¥±±Ñ××wûöí XgásõõõVVVIIIXù,¹rSZZŠŽ[#**jhhXVV†®§Ñh~~~èSTŒ«€_þêokkò[wúåz*•$'''##;|ÚA_vwwoߟ]\\\YY}Zê›?:Lœ øIttôäɓ]]]±+ÑhŽžžžÜÜÜC‡͙3ë8|þŠŒŒyòë °Lee¥………PFFT6lÕßßrùòe‰Äm• à(n7ŠŠŠ ftû€§‘H$??¿Ó§O;88@—¶zýúµ¹¹¹®®îŋ¹vŒ>À\=Š·DDDÂÃÃ]]]¯^œ ¿ ïjhhðòòZŒx1‘H„O2[õõõ={öþýû¿þú+:è ÏàÊ àR:::+V¬8}ú4ÖA¥çϟÛÛÛïÛ·ÏÓÓ*¶zúô©™™™ŒŒ<úÀ3ÖqöàÊ à^®®®[·nœwïÞÒ¥K±ÎÀÈdee%%%]œzULL ë,ü¬¥¥åÀœœœ—/_Ç:àPÜ®µ~ýúäädŒŒCt:ýèÑ£uuuW®\™0aÖqø™LŽ Ò××Ç:Ë-ZÔÚÚ*$$$--={öìyóæ©©©õôô`k\€âp5aaáÈÈH77·””ž°ž•JݶmÛʕ+°ÎÂÏîß¿Ÿzõꌌ ®­ edd=z„.ÿûßÿÆáp“&M’——Ç6Õ8Å àvÁÌÌìøñã~~~Xg`8[¶lY³f ÖYøÖ»wïBCCEDD._ŸÌ·üúúú(Ê?ÿü#&&& @£Ñ™|ø°¥¥EEEeþüùóçÏ×ÒÒbÜ@§Ñh3f̘3gÎ¥K—øif+žÅ àoÞŒñ÷÷OMMåÚ[ì`|*++swwWPPÀ: ¿ééé¹téRNNΖ-[Œ1LÒ××WRRRXXx÷îݚšIIÉyóæÍŸ?á…“'OþÚ«ª««åää8™  žŒ„H$ŸyófïÞœXà?JKKœŒŒ.]º$--uŸB¥RÓÒÒ¶lÙ²aÃLž'`Ülzûö-•JÕÔԜ?þÒ¥KááMîÅ à1îîŠ+W®Ä:Hii©··÷¥K—àn) õôô\žpL&oݺÕÜܜ“eMkkkQQQaaá³gÏZ[[åääЛMcK@qxLOO¹¹yBBŒC °URRâëë{éÒ%III¬³ð‰¶¶¶žžž¿þúËÑÑqýúõlCŸq³éÁƒ BBBh_àE‹Mš4‰Ý{ìÅ à=UUUŸŸŸD":߬ •Íå˗áK޶¶ÆÆÆÞ»woëÖ­ëÖ­cëÕôfÓÝ»wËËËÑÒҚ?ŸÁŽiÓØ·SÀaPܞD&“?~|ðàA¬ƒ€ñš€€ÄÇÇç·ß~ƒñþÇ®ºº:**êÝ»wìØEKKˋ/îÞœûìÙ³îînžÙÄ÷ žŒÊ××wÕªU0Zà°÷ïßoݺõòåËÓ§OÇ: o{úô):3®§§ç(2FoooiiéÝ»wÑét777ww÷¹sçb…ÿÕÖÖ>~üž°°ð͛7ííí³fÍ200 ‚îÛ`D ž>ž7€GAqøÍ?ü ##“””„uÀ“úûû·nÝzèÐ!999¬³ §§§ÇÍÍMBBBEE%&&†16F SRRwvvîèè@×ãpž€€$ ,,¬¯¯_\\ ¥  ý’Qõõõ‰ˆˆ ,}C£c€7AqÆOOÏ-[¶,\žPOOë,€[tttxzzþöÛo<:ǬY³.\H¥RMMMÃÂÂø8 Çãñ߬QМðÓÔÔT^^Î(‰5•J>}ú—ՏœœÜ€I“Æ5;;{þüùŒÍ8ÃÙO¹üDÀxÑÞÞncc“œœ,**ŠuÀ¶mÛæèèžlÙ2¬ƒ 6Nîb`õ6ÝcCŒˆF£ }yŒ™a³gÏnkkÓÖ֎ˆˆÐÕÕEW²ûŽ“ânK0‘ððpWW׫W¯Âððà… \XÙvcæFX{{{ccãǏ=zÔØØX__ßÜÜ<°Ú÷™Ñ ZVV–N§×ÖÖÖÖÖ>yòDFFfϞ=666Ì€âLÂä^ø Z‚âŒ#:::+V¬8}úާ§'ÖY–Š‹‹oÜžÁ˜ €ADDDDDD‡iC§ÓÑÒ§¡¡¡±±±ºººººšJ¥¢ßmlllllܺuë®]»`( ÎãÉ;͌š««kQQÑœ{÷°0ÓÕÕåççó;‚1Âáp’’’ŠŠŠ[¶l±²²êëëCDDDDVV–@ 899¥§§s ô£ˆŽ+®)--ݰaƒ„„„˜˜˜……ECCº~ȉK gΜ5dK&÷òe³!wÊí3­b4í˜ioo_œzuCCÖA6ÜÝÝóòò°N1œq2éÿœÍOŸ>͞=ÛÁÁ!77wàÄvÌŒÓA¿Žç̙“——×ÙÙÙÜÜìîîŸuëVtœŒŒLjjjWW׫W¯lmmŸ–L&KIIedd|³å7÷2šÙ×6µvíÚ²²²ööö_~ùå»ïŸ311yûö-úå?ü0Ìk÷íÛghhøáÇ>¬ZµŠÉR„H$‰DfZÒét(nÀxT\\liiI£Ñ°8-''gߟ}X§ø¯ÎÎÎÒÒÒ¿ÿþ;111((ÈÒÒÒÀÀয়~Â:'ð_qó5£(njii‘““C—‡œžA3gÎÈÊÊ>|ø±òk-™ÙË f_Û‡gZQqÃ󝆝+W®|øðÁÏÏë €sêë띝ÓÓÓ¹dôے’’%K–Lž<¹«««µµA)SŠüøã’’’===X§c—ÊÊʆ†EEE99¹€€$¬ãp3OK êÃûøñ €§OŸ655!2aÂô†×“'O:”ŸŸ/..ehhˆŸVYYÙÚÚúðáÌ-|­%3{Ԍ™M}íË!_‹Ç㛛›'OžŒ Hww7Z }óÇ8¢§¥àÊ ¿\\\îܹƒu À!4ÍÊʪ€€ë ÿc`WS!!!CCÃÞÞ^¬C±]MMÍ©S§Ö­[PZZŠu¶cæÊ Z 0ššš\ºt©±±±¯¯ïóçσ~YÓhެ¬¬3f _"òþý{UUÕ£GÚ젖LîeP³¯ítàw‡ÿrÐk9på:ƒñ+**êèÑ£Ÿ>}Â:àô¯Fuuu¬ƒüWyy¹ššúX²ÐªU«²²²&Näÿ‡XeddŒŒŒÈd²œœýùó獌Œ¢¢¢±Î…%IIÉW¯^1Ÿìèè˜:uª°°pUUÕ¶mÛëÍÌ̞>}Š^ØØWNNîï¿ÿ>þ|xxø0-™ÜË f_Û)3†|-:ÓjMMMMM »fZe²€/•––š™™õõõa°WII Wõ𚯯 ÜŽiSii©ŠŠŠÐúõëûûû±Î…ŸŸŸ7nlÚŽiãÆ×¯_ç¿kWÌ|ö"##ÅÄÄ¿”³³³gϞ-((š  ÍXŸ––6wî\<¯««ËèÏønmm­ŠŠfhhè×Z2¹—A͆ßéð_ùÚ®®.—iÓŠ)))8qBPPðÛ?DèsÀˆ‰Ä7oÞìÝ»ë €]h4š……ů¿þÊáAñ‡ÔÞÞ^QQ€­­ HJJÊõë×ûí7x4œ©©‰D"ÝŒySVVÖÑёo†‡'CNó3­ŽšÏ ܖ㝕•Ummí7°ØåäɓVVV˜W6t:D"mذáûï¿¿rå ZÙ bmmœœ • ‚ ÓŠMsqq!‰®®®FFFõõõXç¬çããSWWWUUÅŠi_ùÿæ.ßinnN `ö`þSRRòðáCÌÿn~öìÙþýûŒŒ²³³‡Ÿœ ¢©©B£ÑòòòöîÝÛÖÖ¶~ýzSSS–Ìm ž»§}…ÛR ‚TUUùøøH$øÅÃOžá†TWWWhhhCCCDD„žž8V1xZKKKfffVV–žžøŠM› °N42p[Š%à¶#ŠššèääŠuÀJ'Nœ°¶¶Æ°²¹}û¶¹¹ù¿þõ¯sçÎAe3j¢¢¢›7o&‰wïÞ]·n]DDD]]Ö¹÷‚â€ÿ066nkkËÍÍÅ:`’’’ǏÛÚÚb²w*•™™™––öóÏ?c’ÿ(**\¿~}Ù²e477¿|ùrgg'ֹׁ>7üWDD„………ŠŠæð³îG£Ñ~ýõWLö^QQáíííââ²víZLð7ƒ®®®¬¬¬Í›7‹ˆˆlÚŽé_ÿúôË((nø/AAÁ˜˜777Ndd€ &7€’““322âãã§OŸÎùœ+x<ÞÒÒÒÒÒ²ºº:99ùÔ©S ,pppPRRÂ:ÀܖàÈËËïØ±cÿþýX£WRRRXXhccÃáýö÷÷Ÿxñ"%%*N’““ ÈÊÊ266ŽŽŽ655oooÇ:À <-ÀöìÙ³dÉvŒŸØF£™šš&$$pžŒhllܶm›œœœ¹¹9'÷ ŸÔÝÝM&“I$Òĉ1¿]åààÐÕՅaþP]]hllÌLc(nB__Ÿ……ETT\ßæ9±±±BBBœÜéóçÏýýýOž<©¥¥ÅÉý‚áÕÔÔ C„-[¶š©©ap7 ­®®ÎÅÅ%55Æ ã!õõõ[·nÍÌÌää_ê)))×®]‹G§ã\ˆB¡$%%­^œzÓŠMX'ìÅ _•———••uâÄ ¬ƒfmÙ²ÅËËkΜ9œÙ]ÿŸ}ûùå—_  #·£R©ÿþ÷¿I$F³¶¶644s°Op6ðU?ýô“ˆˆHjj*ÖAS €€€8VÙ455YYY-^Œ8<<*ž0iÒ$ccã˗/Ÿ>}º¶¶ÖÎÎÎÛÛûÙ³gXç¬WnF³ŽŽ<|øðìÙ³±Î†Ó××gfföûᅨˆˆp`wïÞœsuu=vìcþKÀ‹^Ÿ|yùòåǏ¯\¹ÒÙÙYJJ ëD€5 žàêëëÒÒÒðx<ÖYÀWEDDš««sæI¥ââb??¿øøxyyyì°:CgJJ :C§™™ sÅë žàÛþþûo‰tæÌ¬ƒ€¡œÿÞÇLJ37óòòΜ9sñâEQQQìp¯ÏÐ  ž€)¿üò‹‚‚‚œœ=ÖAÀìì씕•Ùœ£ÔÔԌŒŒ„„„)Sа{_C%%%W¯^}üøñòåË0œ{Œ70}Œ"44TSSë,àäææ>~ü}j‰­¢¢¢*++###¡ûð8A£ÑîÝ»wåʕÚÚZssó 6 a 0Š˜õùóç͛7‰DøŽ{tuu™™™eff²õRJ¿···¢¢âîݻٷÀµÐ:‰D¢°°0ÌÐÉ ž`þùçŸ_ýõâŋXÿ±ÿþ¥K—²oT*ÕÙÙÙ‚}{ù™L—““ãäN9 ººÚÇÇÇÒÒë £ÎÐYPP0þ|˜¡“›AqÀÈ?~\RRÒÑÑë )//?pà@RRûvÑÓÓãäääääŽråJöíeH$ A-†Áï«°°ð·ß~+//_»v­g ̃;nj̮]»þüóOø‹ìÛ·/$$„}Ûïì쎱±Ù¶mç+ÀåæÏŸòäɔ””iÓŠmÙ²ÅÎÎîæÍ›p±€{@qÀÈàpž˜˜˜={ö޶¶be\»ÿŸ¢¢¢ŠŠ ›¶ßÑѱiÓŠ€€€+V°i€×M™2ÅÒÒ2%%åøñã ÅÄÄdϞ=eeeXçPÜ0rbbb‡rwwÇ:ÈøE§Óùå—={ö°iûÍÍÍVVVK–,aÓ.?‘••õòò"“Éööö †††QQQŸ?Æ:×øÅ £±pá ÄÅÅadœúí·ßŒÙ4Œ^SS“œœý‘#G.\Ȏí³IAAŒŒü §xà¡#GŽÉÌÌTVVöööÞžq#™LîëëÃ:מÅ £äééùðáÃ'Ož`dÜéêêJNNÞ²e ;6þñãG›ˆˆvlŸ}‚‚‚ÐnPÓ`kÐ 7nôöö~þü9Ö¹ÆxZ €Ñkoo·±±INN†‘ø9éСC‹-Zœz5Ë·\[[ëììà øŽô©"11±úúúI“& \‰ÃqÝòüñŽÔH¡3t¯\¹ÒÎÎfèd7žrÀ艈ˆ„‡‡»ººrÛï>öñãÇââbvT6 NNN\RٌBKKË Êp---ôv@ ²¶¶&‘HœœœÛŒy󊶶«„|ŠÆDGGgŊ§OŸÆ:ÈxŒÿ~–o¶¥¥ÅÁÁáÔ©S}údgggdd”žžž››ÛÐÐ`llŒ}ûvŽœœýÞœ{?þœ–––••…® «ªªzñâE~~~ff&§Þ"_ÛŒysJJŠ··wjjª±±ñ‘#GÊÊʪ««MMMããã±Èûè€1koo_œzuCCÖAøœ……Emm-k·ÙÙÙijjúèÑ#ÖnvìˆD"‘HdŸ=ò¿Å cB¡ ËÅÅÅ***Œõ555èrGG‚ ŒlGGýÚ9` IDATG—åå壣£ßœ{7pGÊÊʯ^œB—)ÊHŒô}ýýý;vìD/KHHlݺµ¿¿ë\< ®ÜÀ‘‘‘nnntè|Ã6YYY‹-š1c ·I¥RvíÚµ`Án–«(++£ ***>|`¬—‘‘AЉ`?X!!¡®®.t9###//OWWW]]ý?þ@WÖÔÔ0nÞ16ÆB@@   €Ñ§±±ñ÷ß_³fMgg'¶Áx×D¬À'‚™™ÙñãÇýüü°Î‡h4Zllljj* ·Ùßßïâââææ¶|ùrn–ÛTTThjj"R^^>sæÌœVOO/==N§çää8;;£Ý]eee+**Ð[xåååìÈ<ÞÐétaaa:Î(q*** TUU±ÍÆúúú>>>lÝ7°Œ««k~~>ÿ²ÄDJJЉ‰ gÕi4ږ-[¬¬¬ø~vŸóçÏ£ 7nÑkÍÌÌ8€ÖFŒŽÃ¶¶¶ŸŸŸ ‚øúú²:ïx„Ãárss¿\omm’’Âù<ìfmmÍî]Àm)X)**êèÑ£Ÿ>}Â:_éïï¿|ù²““ ·éãã³víZ###n“;™˜˜,_Ÿ|Μ9***AAA#z­ƒƒƒ“““žžøþýû“““Ñ•ûö퓗—×ÖÖ600Xœz5£§Ü®ÜÀJSŠL9qℋ‹Kjjê„ °ŽÃ'7nÜÈÂA\>¬¬¬ÌgãÈ ìïÅXFvìØ1LãaŸ477777ôÚ)SŠÄÅÅ¡sÉÉɱ =,Wn`1555[[Û#GŽ`„OP©ÔŒŒ [[[Vm099¹³³ÓËˋU‡|||êêꪪª|}}ÍḬ̀ŽÀ`PÜÀzVVVµµµ7nÜÀ:?ˆ‹‹srrbÕe°[·nݺuëСC,ÙÚž5kÖ¬… .ZŽHYY9,, ë8 ·¥`‹ÈÈHsss ++‹uÖÕÕuãÆ V÷øñãžžž+W®À ºcäåå—ŸÆ3.œ³lžr[Lž<966ÖÝÝœ¿¿ë,<,**jçΝ,©EÞŸ}rá…‰á:ø7°‹¢¢¢““Shh(ÖAxUKKËýû÷Y2Gæ§OŸÜÝÝϟ?/""2ö­ÀÆóJ(n`#ccã¶¶¶!G°ßɒ‘Ÿ:::Ϟ=;}úô±o Àý ž€œ"""Ξ=[UU…uÓÐÐPRRòã?Žq;t:}çΝû÷Àž2p¢xtM___pp°¢¢¢ŒŒLLL £3óÃS©Ô;wJHHšªªÆÆÆbòŽFŠØKPP0&&fç͌QÕ3Ž?Ÿ{÷î±o'((hÕªUK–,ûŠà!ôÿ?AˆˆˆüüüŒŒŒ§OŸ3Z23?üáÇKKK‹ŠŠnߟM"‘8ÿvFŠÛ;<Àrrrîܹƒß0éóçÏnnncxþÒ¥KUUUÁÁÁ,IÅ>=ÊÊʪ««ûøñccccWWWGGGKK‹‚‚‚‚‚ÖéX¬ºº:00ÐØØë <`ŒÓ/ zŠIMM-==][[{P›šštÕÎÎNaaáÚÚZtÕÎÎNIIItòNUUÕëׯkii!R\\¬££3–âÓJÀSp‚‘‘ѝ;w222`Ä3fœ:uÊÃÃcŒ)((žyóæå˗Y‰­ž0nìòÄ\žp[  »páBEEÖAž]kk+…B100ËF*++>ÇŒ8::2~£ ¢   • £A~yyù²²²ÑmjæÌ™Œÿ»ÞŸ};ÖdìÅ 2qâÄøøx///*•Šu®vúôé;wŽe ­­­Û·o?wîú—(—ËË˳³³ÓÕÕ@DII)//oîܹXç>>>|øðჷ·7«“²7pΌ3Œœœ÷ìكuîÕÑÑñÏ?ÿüôÓO£ÞB¿‹‹KDDÄ̙3YŒåèt:™L611¹sçNBBBLLŒ„„„¶¶öƒTTT°NøÁž={–.]Êž~ãçç·téÒ~øAOOoΜ9#ÚÔÞœ{•””æÌ™óý÷߯_¿ž aY :ÀiÁÁÁsæÌÙ°aÖAžÑñãÇ ‚¡¡áš·àëë»bÅ nî¯J£Ñ®]»vñâÅÕ«WoÛ¶qy)11ÑÂÂbêÔ©ØÆ܃o1Á÷WnàŽ«W¯–””`„ëtwwߟ}{͚5£ÞBrr²„„×V6===—/_^»vmSSSffŠ——×ÀgŽŽŽPÙÀPÜÀi±±±ÞÞތ'*!!aëÖ­£îüìÙ³ììì   ÖŠb‰ÞÞޘ˜˜õë×÷ÝwÙÙÙ...‚‚‚X‡€oAq€¥¥÷ìÙãçç‡u.ÒÛۛ““cbb2º—þü900066–ۏ¢Óé$ÉÈÈhâĉׯ_777G;ØÎ1°ñÃ?ÈÈÈ$%%a„[$&&nÞŒyt¿øûûû]]]###EEEYl,nݺehhX^^ž••åââe œƒø€™ÀÀ@kkë hjjbcýýýׯ_ÝËlllÐáS¹…B9~üøôéӉD"ô€€Ã ž3qqq›7o&‰<1" û€¥¥™™™M˜0a¯œzõêwß}Ç=§VVV†„„ˆ‰‰=zTJJ ë8ŒGP܀%qqñýû÷ïܹóâŋXgÁÒo¿ýöûᅬâ…EEEׯ_ONNfy€Qèêê:zôhIIIXXLB† žc‹/ÎÏÏOLLtttÄ: 6îܹ³`Á<?Ò¶ŽŽüþûïÜЗ…L&Ÿ9sÆËËëÀXg|âýû÷<17‚AüÀNßžq£¿¿ÿŒyó°Î‚›3gÎHJJŽô…›6mòõõÕÓÓcG*敕•iiiNž<Û0€ŸÉäîîn¬S°žšš»ÿ¯ƒ+7`‡ÃÅÄÄØØØŒÃΧ¥¥¥ÒÒÒ£šl¢¢¢Ÿÿþ{l+›îîîÇWUUyòë ræÌ™;vŒè%t:}çΝ˜Ü¿£Ó鉉‰Û·o?uêT6p'(nà.QQQÁÁÁ---Xa»ÎÎÎ/^èëëèUǎ[µj&=¯ßœ{giiÙÕՕ‘‘¡££Ãù&Aqw wuuåû'/^Œèàà0¢—Üœ{÷͛7˜<3O"‘<<<Ž9âææÆ Ož†‚ÀâââzzzFÑӖWÐh4ccc2™Ì|¡ÐÔÔdkk›––Æáќkkk}||tuuýüü ¬€'À‰ 7ruu-**ºwïÖAØå?þX³f͈jŸ#GŽpž²!‘H®®®aaaPÙÀ+à\€KEEE…††666b„-Ð9À™o?þ|Nvµ©­­µ±±)//ÏÈÈéó\lÁ£àp)aaáÈÈH77·””‡uVzóæ¬¬¬šš(óíoÞŒ™’’ÂÖT‘H€€€€ÈÈH555ì.66ö¯¿þâÀŽž™ŸŸŸÖ)Ÿ€âîE ÌÌ̎?îçç‡uV:s挛›“{zz<<<.]ºÄ™ ¯®®ÎÇÇgÞŒy»uûöm"‘È™}q-kkk¬#þ·¥àjvvveeeùùùXa™¶¶¶šš &ÛïÙ³g÷îÝÓ§Ogk*TbbâöíÛùåèaOƒ³nuôèÑOŸ>a„5.]ºÄ|o›ÜÜÜ)SŠüüóÏl„ HSS“œœ}KKKzzº²²2»w`+(nàvSŠL9qℋ‹K?ÖYƊN§ggg1Óž®®î̙3¡¡¡ìNuÿþýM›6íÚµËËˋϺ70>AqPSS³µµ=räÖAÆêƍ?ÿüó„ ˜iìååuâÄ AAAöåéëë ‰'‰˜Œz `(nà VVVµµµ7nÜÀ:Șœ?ÞÉɉ™–qqq+V¬PWWg_˜ÊÊÊ 6èéé]ŒxQXX˜};b­û÷ï«««è Ó×Ãe*À¯ài)xFdd€¹¹9@••Å:Ëh”——‹‹‹‹‹‹3ÓòÏ?ÿLMMe_‰”’’röìٙ3g²o/,„Ãýg@yÿˆˆsss¬ÀœàÊ >~ûöíßlF£Ñ|||¢¢¢Øt]¡µµÕÉɉB¡‰D^©laL•STTÄd§%Æ-(nà%ŠŠŠNNNècËrT*õÕ«WsçÎýfË#GŽØÙÙÉÉɱ#Æ?ÿückkëáá£{·¶¶Nž<ëp5ž<·όÛÚÚrss±22ŠŠŠßlöìÙ³²²2vŒçÖßߟ’’¢§§Çòí³z‹ñïÿ±wçP­ïÀÏØb$Ùº–$*-ߎ©{+KÙ²‹ ѕŠŽ)ÕMEÅMi'Kšî-RŽÜ6%¢®”0„PÙfl¿?Îï;??1ÆÌ™93ãùüuæõžç}4÷œóŸèÁû÷ï­¬¬€€€ÆŽ»jÕª††Ž3…Bñòò’’’š4iRtt45È`í!66VQQ-øBttŽ––‘HÔÔÔÌÏϏWSSÑÓÓ+--EÏÊÎÎÖÕÕ%‰S§NœtéÚH&“===¥€€TUU£¢¢à¶€^·¡P(+W®üôéމ êU«ZZZh÷éèè044üúõ+棓H$SSÓäädÌ#cÂÆÆfÈ>Ô×}nkkkçä䎵µ577{{{¯_¿mßµk×Ò¥K«ªª*++—,YB=e°vAÌÌ̪««©/W¬XQVVÖÒÒ²ÿ~111SSÓ> /õõõÑnrrrÉÉÉííí%%%öööhcpp°‘‘QuuuuuõòåËéÿ-CÏ?t‚â®ôùóç+VP(Œ¡KYY™··÷Ý¶nÝú÷ßc>znn®±±qYY摱ÂpqÓ×·o߯«ªª£ÇoÞŒ¡ž2X;‚ }keAH$zÜÚڊ HMM õ¥ˆˆz¬ššùùóçŸiššš””” ÇÅÅÅPÜ\Àe)ž’¢¢âƍwî܉w"t‰‰‰qqq¡ÝçŸþimm]¶l†ãööö:t(>>>99YUUÃÈâùóç¿ýö›€€$@¯©©AÛ«««©ë,÷ÝÒ|°vA&L˜Ð÷¥œœz@$‘••¥ŸlooGÓÒÒrrrtttÔÔÔnݺ…6’H$eeeôÖzxân…>2“––†w"C P(¥¥¥:::4úÉäœ{÷†……a8îׯ_mmmϝ;'""‚adÎaggçìì\VVÖÕÕÕØØH}ŒNAAáãǏèñ‡šýkGZóFWW755µŸŸþرcÔâU^^ž:Dyyùpc€ (nàb¡¡¡gϞ¥þ.áL©©©æææŽûìܹÓÇÇGLL «AŸ?îèèâàà€ULÔÚÚ:fÌQQъŠ 777j»ƒƒƒŸŸzãËæÍ›‡lgŒ¹¹yAA™LFúÔFöööþþþ$‰D"ùûû39Œâ.& ãëëK¡PðÎePW¯^µ±±¡ÑáŸþikk300Àjʘ˜èèèäädMMM¬brŠžžžmÛ¶‰‰‰-Y²D__ŸÚ€¬¬¬­­œhÑ"KKË!ÛãììŒnÝ:IIɝ;w^Œxm VTTÔÔÔ\°`KwÏ`0ÿ»ä%€{åäädff=zïDðï¿ÿFFFž8qb°d2yÕªUW®\ÁdÚæû÷ïÞÞÞ3gÎôõõe>ÛØÚÚ^œzï,°WXXhnnNçÅ)^ýGž€™žÞ¯¿þ:zôh–nVÀ°sçιººÒè‚Õ©W¯^ÙÙÙùûûsWeÃ{üüüjkk+**üýý‡Œ" +ÀÞRð‚Ý»w[[[kii©««ãËÿééé)..бÛöÓ§OüøÉ©žžžÜÜÜ+W®Œ3†ùh€'Nœ={6…B133 Å;0Aq/àã㋎ŽvvvNIIáœ'ƒþú믥K—öÕ®®®={öPïÕ`XWW×®]»ÆŒsþüyX—øúúÂäÀ\–€GŒ7nÛ¶mx'ò.^Œhoo?ØW:äææ6vìXf† ‘H‹/ „Ê€‚âÞ¡¯¯/''—˜˜ˆw"‚ AZZzÀ¯ŸÿþÝ»wLސñðáÃõë×GFFb»ô€ÛÁe)xÊöíÛmmmg͚5eÊ|3¹téÒ`Ó6œœœÛ¶m£ñ=bbb=z”œœŒ.¡ T0sOáãã;}út@@@[[Ÿ™ÜŸ}Ý7ñggΜY±b…‚‚c‘Éd²‡‡GSSS||ŸÔÞÞ~ìØ1¶1§P(>>>ššš§OŸÆ"AÜŽŽŽÜºu+33³¥¥ERR²¥¥…ŸŸ_YYùúõëÚÚÚxgï€ËRð²gϞ}ÚÝݝ=#¶¶¶R(”Ÿ+›²²²ÏŸ?ïÝ»wXъŠŠüýý#""p²õõõ·nݺ{÷nww·Á©S§ún¡µ}ûö²²²7â˜!Œ fnà}®®®^^^ºººl+11QPPÐÎή_»­­íÑ£G‡õø÷­[·Îž=#!!iެõéÓ§ôôô{÷î;ÖÒÒÒÀÀ@HHï€Y ž€÷µŽŽØÙÙ]ŒxQ\\œÕcYYY%$$ˆŠŠömŒzõjmmí°n˜ˆˆ(--ŒŒÄ:G–(..ÎÌÌ,,,”‘‘Y¹råâŋ|ÀðÞ7zôè°°0ww÷˗/³tŠºº:qqñ~•͏?âãã322è B&“œŒŒf̘͂±ÔÓÓSPP‘‘ñôéÓɓ'[[[oݺ¶žw0sÀHqúôi2™ìããú!Ž?>uêÔ~ oÙ²ÅÊÊJOOž nnn>>>K–,aMŽèîî~üøqRRRIIÉŒyólmm¹ñ– xÌÜ0Rž»»¯_¿þÑ£GóæÍcÑ999ý–¡{óæM{{;•Í«W¯¶oß~êÔ)Ö$ȔŽŽŽ;wîܺu«¶¶V__? `üøñx'7Œ –––/^”’’Â<øÇ•••ùùù©-œœœ;vì sˆÌÌ̄„„«W¯²yUž!577ß¹sçΝ; úúú!!!²²²x' .K0²ïÙ³çêÕ«˜ßrèСyóæ-\žÚËÇÇçââ2ä¹111uûpCCÃ͛7³²²º»»MMMÍÍÍ9­ê¥¡¡áÞœ{xg1rY[[÷} Å #Î¥K—ª««° »råÊôôtêÌMccãš5knÜžÁÇÇGã¬îîî-[¶Œ;v÷îÝØæÃ˜ŠŠŠŽŽŽG‰ŠŠ÷»?€%%%=yòdîܹx'2;vìÑ£G}[à²#Žƒƒƒ»»{^^^ßY&}úôIEE¥ï5©;wîß¿ŸveÓÒÒâææfgggff†U&Œ)//ÏÈÈxò䉾žøŠ+6nÜÈ9sH€[̝;·ßü`äää~-PÜ0EDDX[[khhÈÈÈ`0))ÉÊʊúòõë×|||Ó§O§q ‰DÚ°aÃîÝ»g͚…I (..NJJzöìÙ€I“¬­­}||àAnx7ŒDÂÂÂGݰaCrrrßé†åååùûûS_îß¿?**ŠFÿÂÂÂíÛ·GGGO˜0ùч…ú ÷»wïæÎkbbÂ!WÄXâ€jòäÉööö B€µµ•á›K*++•””šEÒõë×õôô€¥¥럑‘‘˜˜xåʕŸ{-±ZGGG~~þ͛7ß¿?kÖ,ooïɓ'³mt;AqÀÈeccãíí}çΝ'OžDDDTWW5Ё8W¯^¥^“¢P(±±±iiiƒuŽŠŠ*))¹té{v'hkkËÎÎNMMýòåËòåËäääØ0.܂@àÁG‹ ž`DÛœ{·ŸŸ~UUUWW×­[·ÌÍÍ’——çç燇‡‡{{{X$õöö¢»‚Ÿ8q‚™œéÑØØ˜™™™M&“=:vìXV Àx²’à@PÜ0rYZZ–••õôô Ã@qSYY©ššˆ^“úòå˳g϶oßþs7 …âááñ믿:::2Ÿ9dnݺ•ŸŸßÝÝm``pêÔ©Ñ£G³n8†‹Û+n)Πž`äêììD„ŸŸ-nJKK»ºº†{µèÚµkÔkR;wîܳgÏÏ}~üøáâââêêjhhÈtÖøøñã7rssÅÅÅ---ccc…„„X1€+ÐZ‚ÀÛttt^¿~íìì–ØÖÖ&**ZSSƒn7ÛÖÖ&--ÝÖֆ Ȅ ÌÍͩ窪ªfee¡ÿ»ßŸ};mÚ4zª[[Û«W¯þ¿ ž€ÛÙÚÚö]˜uuuè%ªáêîîF“õ©Ž;–}÷îݔ””úúúe˖YYY)((`8KaøŸ€Ÿ’““ûý&ã 7ííí‚tttHHHŽ··#"""ÒÔÔDm¡]Ü äùó灁MMM‚ðóó °Ð IDATwuuõ Þ7ˆŠŠŠ­­íšD" …âããCÿÓYÜôí3ØË—/_îÛ·///ORR2""ÂÈÈͰ¹¹}ÜrÈoŸêçân(€ðäŽ6gΜqqq144 —””Ä;Fðäû‚»Ÿ7âR?~œ2e ‚ åååÔª]AAÚþáÃƂØÙم„„$%%‰‹‹ÿþúßGQQ±¬¬¬ßÌ ‚ ÷ïß_²d‰„„u?Ý &ÜŸ}{âĉýzbž?‰®®njjjooï͛7]\\jjj‘——ÿøñ#:sS^^Îppž¡À¡$$$._ŸìììÌ¥• àUÝÝÝè†LòóóCo.ñóó[œz5Úèàà@mߌy3cAZ[[nj#**ZQQáææFíŒvíÚM›6•——×ÖÖnÜž‘Ú>~üøû÷ïÇÅŅ……¡-žžžnnn%%%mmmϞ=£nm+--]RRÂü÷Ž ˆ¹¹yAA™LFúÔLöööþþþ$‰D"õÝÑež ž†!<<|üøñK–,9uêTUUÃqLMM.\š­­­ªªºcÇŽ1((HYYY[[{Ñ¢E–––Œ‰‹‹Û¶m›˜˜Ø’%Kôõõ©æÍ›§¯¯¯«««­­Ý7ŽŒŒ|nnnbbâŸ}ûÙŽi“©©éªU«ddd6nÜèêêŠvÛ¶mÛŒyó0™¿qvv^·n€€äΝ;/^Œˆ6+**jjj.X°ÀÀÀ@PP±àpÏ \ïçëÍŒÛ¿/nϟcáþ·~ýzAøøø€¥¥ÅÅÅ555ÕÕÕuuué¿IçjxŽ»q˒zŒ),,477§çâÜsoÿžämðÞa¥»»»±¯_¿ö}9jÔ(!!! …ÒÓÓSWW×ÔÔôýû÷?~èêêâ8/ðóó $“ÉþþþŒmƒ@q8‚r„ÍÚÛۛšššššjjjH$RSÔn e̘1ÿ%//?kÖ,ôXZZúÙ³g©©©h™uëÖmÞŒùæÍ›¬K{ÀË@žrX”ÕĉgϞM¡PÌÌÌBCC Å à÷ßOŒ-Y~®Wh”,òòòrrr***Ԓ…þ…­¥€€‚žžÞΝ; »…ηžºn3AX„E£ûúúúúú2ŠÜ€²²ÒÆÆ†úRPPP²))©É“'S_b»öJMM­°°°ïÒs€ÓÀÓRŒP!11qÚŽi¢¢¢zzzEEE?wÀƒ^‡íNC‚ k§¡ß»7¢‘?û?$ŠŠŠ×úžxñâ‰'öìÙãëëëääŽbÅ ===uuuVT6‚ðññAeÃá ž`äJIIÉÊʪ««322òððø¹CVVVZZZ}}œƒƒƒ±±qjjêíÛ·LLLšýœœ‚‚‚SRR233ÑÆÐÐЊŠŠ7oÞäå奧§³ï[ÂÉŒyóæÏŸŸ˜˜ØwQ×áJMM}ðàÁëׯߜ{G]näÀïß¿/,,ÌÍÍE×Àe ˆµµµ——WUUUEE…‚‚ÂöíÛÑöß»ãÇçæææää|øð¡³³3$$„öˆð!œˆž-©œìçMãè HMM zÜÚڊ.sÞûÿ7ö#‘HÔƒõWTTŒŒŒüüùsßà***%%%èqqq1c?jûŸp1}útA„„„õõõSSSÉd2=ù÷ý×...F‹ŠŠTUUÑcUUUjû›7ohÿK€¯oߟ?=ðœÓÐÐ(--Ekkk•””hÈþ g~0èÜ8°lœ blُÁ6|ùù€v 7ˆégùòåcǎîYž(..~ûö-õ%‘HäããÓÖÖ~øð!íñÝihÀ÷n°M…hçOû%¶Ü×¹{KV€uncÝ1ø;l@3fÌ@DDDdeeUUUœŒŒš‹®Ò‰ý; øÞ ¶©3Xº‹‡˜$ÜþÁl‚XÂÏϯ¶¶¶¢¢‚™ boƒ `žçÀ˜lÄþ†8y!~Å €%0Ù fä ³P襹Óë°hDøËRà)P܀§@qžÅ €CýøñãâŋÍÍÍx'à2pC1€C‰ˆˆ úøøŽ··/[¶ÌÌÌì—_~Á;)€?2™ÜÖÖF$ñNp.Þ\ €%::úÞœ{l”B¡ttttttˆŠŠŠŠŠvuuÕÖ֎?þçn]]]Œý>þ<‚ d29//ïæÍ›ïß¿Ÿ5k–ƒƒƒššß‹áòŸŒòòò_¿~mmmUPP˜;w®žžžŠŠ ÞIÎÅ €ÕÕՇ¶··ÏÎÎ BÄÆÆæøñãòòò}»‘H$WW×}ûö͚5‹ùA{zz=z”™™YRR¢££cbb2sæLæÃ.USSóüùó/^Q(]]ݙ3g.\ž[6“¬Å €AVVVIIIæææèÆŠÿþûï¡C‡Ð}ûjiiquuµ°°°³³Ãpôââ €gϞMš4ÉÚÚzþüù.4Fˆ®®®wïÞ=|øðɓ'---"""óçϟ?þÔ©Sáƒ1Aq`¥¥errò–-[6lØ ¡¡ H@@€‰‰É¢E‹úõìîîÞµkæi”——gddÜ¿_LLÌÊÊÊÐÐPPPóQw©­­}öì:©ÓÙÙ©££3sæÌ HHHà`(n ò÷÷÷õõ%‘H÷îÝÛ±c‚ ?~ü°··OOOçççÿ¹LLÌ˗/Oœ8Á¢âãóçÏ©©©==zŽ¡¡¡±±±šš(+Ü¥»»»ŽŽôŋOž}ŠNêüøñcêÔ©óçϟ7ož””Þ©áâÀ8KKËëׯ£³5Ô+S‚xyy­]»vöìكH&“œŒŒæÏŸ¿nÝ:¶dú¿º»»?~œ””TRR2oÞ<[[Û)Sа3À-èŸÔ¡P(ǏŸ>}:^ق~ ž0ÎÕÕõ?þ””ÜŒy³··7u:€±±qݺuiii4žÂííí=|øpmmí‘#GŒ™¥zzz 222ž>}:yòdx˜ÐFÔyÿþ}KK :©£§§'--ýìÙ³¥K—Šˆˆ¬X±"22ncçPÜ¥ŠŠ¶té҇æååmÛ¶ú¥èèèÑ£G;99юpýúõ˗/ÇÅÅáxÏoqqqfffaa¡ŒŒÌʕ+/^, [ӀAõôô?yòäɓ'ÍÍ͝‚ðóó+((ìß¿ßÑÑïG:(nŒûçŸrss{{{W­Z•ššJýRww·™™Ybbâ+‹Œ~ý:((èĉlžÅ˜¶OŸ>¥§§ß»woìØ±–––BBBøŠ8ßâŋïß¿O}9fÌmmí˗/ÿŒ `(nŒëèèpuuœxñ"‚ ŸŸŸ›6mê{£naaáéÓ§O:5dœšššõë×,^Œ˜uÙÒ¯ŸŸþÖ­[7oÞììì433³°°Ã;)À¡¿~ý*$$ÔÞÞ.$$ÄÏÏ/  ¬¬Œ{±>r~ü=ž0aBUUÕϏGñññíÝ»788xžÁMLL‚ƒƒíììúݪÌá7lؐpøðáöövWWW++«óçÏûö í0gÎô)z¶ ž0KOOïɓ'Ô—ÆÆÆ·oßþ¹›¶¶¶°°07Þjii]ºt)$$€ïޜÜBJJj͚5W¯^={ö¬   ———MllìÓ§Okjj<<<œœœàêÕÈAù QQQªªªÒÒÒÞÞÞÔ¿(Š———””Ô€I“¢££›)ill”‘‘é;£ÙØØøË/¿455õôô„††*++KJJºžžŽ¶¶RGŒUTTD7þì7ÐÏvuu…„„())ÉÉÉEEEQ¿D hÏß„èèh---"‘š©©™ŸŸ¯ŠŠ&""¢§§WZZŠvËÎÎÖÕÕ%‰S§N¥N÷’ÉdOOO)))UUÕšš(†'Š ž0KII©ïò 666IIIö€ٛ~€€€®^œúøñã   οg@cƌ±··¿páBBB?Ð_K ×®]ÓÕÕýôéÞ vKMM}ðàÁëׯߜ{†68pàýû÷………¹¹¹ƒý?BDRRÒÊÊ*&&†Úãàà !!qüøñÜÜܜœœ>tvv†„„PûdffþóÏ?è ïCtèСŒŒŒœœœ‚‚‚¢¢"ä¿W¬z{{‡Œž–•••––V__ïàà`llœššzûö톆Ž““SPPPcccJJJff&ÚZQQñæÍ›ŒŒŒôôt:þÑ L³³³ëèè Ÿ\œzuccã€=ÓÓÓ÷îÝËð@éééfffµµµ GàԟïT¿üòKjj*ÞyÆ]»víÚµkCvë[£ÇEEEªªªè±ªª*µ}q°P%%%&LèìììíííììTVVþôéSoo¯††Fii)Ú§¶¶VII‰:"ځö@ԃI“&œyóf°üi$ =Fçjjjš/EDDÐcEEÅÈÈÈϟ?÷=WEE¥€€=F¯D9\oo¯M¿Ø~€Ã‡/ZŽhîܹèËÌÌÌÊÊJOOÏ;¯]»ÖÇÇGWW—±±þý÷_Ÿ===Óe¥ŒŒŒ°°0ڛ&Ÿxñ¢©©‰ð_‚‚‚‚ˆ‹‹kii±+M®WUUåççgmmw"ÿ ü2áí„öövaaaA:::$$$ÚÛÛijj¢¶£¥À`ьmmm¯\¹’‘‘îòF$ÑP(>>>t²“@ ôôôP/ô 65ß~Ϟï‘ö˗/_îÛ·///ORR2""ÂÈÈ޹¹yÔšQô|ûT?o¿37 ***}c9Ö`»1“V; qé ŀ`…bk=úû÷ï}[~ùå—îî:Ú'®\¹òãǏX]œB „„„üöÛoŠŠŠ?~Ä02¶TTTÐUUÕêêjôžººšÚ>iÒ€!ƒÈÉÉ¡è=˜²²²Ô—ԝ†H$RߘCŽUQQ¡¡¡>#++[YYÉ|iii999:::jjjÔ[¿I$’²²r¿ &€wž2oÞŒGöm\»víùóçÿýwÚç9rÄÉÉ)55UHHÔV®\9}út///777 #cåãǏSŠLA€ŒŒœº ¢‚‚µýǘ $//_^^>uêTAÊÊʆk„ ·oߞ8q"&££tuuÑýÏoÞŒéââRSSƒ&öñãG AèYŽ ‡……Ñs`îÕ«Wý›ð˜@ð¬âââ;vôkìéé144ìééòô¿þúkûöí¬H¬³³sǎ>>>mmm¬ˆOÅÀe)ƒªªªªªªå˗‡„„ í;w/]º”ök„Ÿ­ ƒ‚‚–.]ZYYYYYù믿ÓëøñãK—.}ûömkkëÓ§Oil†Jfff/_ŸlooÏÌ̔““£&fdd„îsd``@Ïï&ž h€ËR,M™2…ú\ @XŒxqnn/_ŸŒ­­íÞœ{˜'& °ÿ~;;;kk낂Ìã3“=†è>>¬É?wîÜŽiÓlllšwÔÜa²£((nCîû¹ÝÅÅåìÙ³ôDÐÔÔÔÒÒ¢®ðÆ ÖÖÖááឞž—/_fÝ(˜ã„=†8yG!PPÜ0Š¡¡ñîÝ»ŸÛ—,Y’ŸŸßÕÕEOßÿ=99¹¢¢ëìþ’’Rjjêçϟׯ_ßÒÒºh£óbS/Çì1Ä¢0ÙQ7ìIHHôÛd µråʌŒ z"ðññEDDøúúöôô`Ýÿáçç ܰaƒ¥¥åϗÒ\ Šö–,Y2à³QkÖ¬¡> 3$EEE‡ýû÷c™Ù@æÌ™“œœœpàÀîînV`5(nØ[¶lـ·Ý‰Ä &Òœç—O†÷#&&£ŠŠfaañó£ìîÅ {²²²èʳ?óññ9}ú4ý¡Ž?þõëWŒR£ÅÊÊ*66vÿþý‡‚)ž7–PRRúôéÓÏí'N¬¯¯oll€3ް°ð¡C‡<==ÙsçìžqãUTTÌÍÍKJJØ0"s#eU(›Ýžq£¡¡ÁÅÅåç/ýõ×_ÅÅÅþþþôG;}útgg§··7v ¡®®n˖-ššš¿ÿþ;???ý'fdd„……?žu¹Aªªª¶oßΙ›…ÜAq`‰?~x{{'$$üü¥ÞÞ^##£¬¬¬a ëÖ­óññÑÑÑÁ.Ç¡%%%?þðáÃ芒®—¥,!&&öýû÷ÿ|"+V¬žuëÖ°?~|Û¶m?~üÀ(AºX[[ÇÅÅ8p@q`--­¢¢¢¿äìì<à€ âââ{÷îõóóÃ"µa Þ…cddôêÕ+6`7VY¶lÙßÿ=à—ÆŒ£ššøúõëaüÏþ3mÚŽ3gÎ`‘ÝðX[[_žp!<<üàÁƒìO@?(n¬¢§§÷øñãÁŸêïïêÔ©áÆôóó{ðàÁ‹/˜KèŽººº©©i~~>ûÐ Š« v—ÌøñãÉdrUUÕpÃFGG±g囟YXX\¿~=''gýúõõõõžä ÷îÝxçàYß¿'‘Hƒ=j€¢¢rîܹ¥K—+АÐŒ3­¬¬Ø¿'6‚ ‚‚‚úúú'Nôññ©¯¯Ÿ3g.i37266Ÿ}ûö`_>}ziié·o߆VSSÓÌÌì?þ`.;ŠhkkgddHHH¬X±âùóç8fèŠ 7®ŠŠ†ÆzZnnngϞe ²ƒƒCyyùÝ»w™ÈŽYÝ ôʕ+~~~l~L0(n¬¥££Cã©(##£ììlƞ?:~üø±cÇH$Ùa@RRòȑ#«V­²²²:þ<ŸÉ(n¬fllœ••5ØW ªU«’““ˆ,,,éééÉ Ïf/\ž033“D"ÙÙÙ}üøïtÑ`ûkõôôXYY¥€€ ցL&[XXÜŒy“±øwîܹqãÆ‰'McŸ>}šÌ/° IDATÚ¹s§‚‚ÂöíÛÅÅÅñN€‘fn¬ÅÇÇ'..NãÉíQ£F-\žðÞœ{ŒÅ_¶l™¢¢bLL £ blâĉ‰‰‰«V­rrrŠˆˆèêêÂ;#F(n,g``ð×_ÑèàááñçŸ2ëÖ­>ÌËËc8ææÌ™“žž.%%ebbrãÆ ŒÓ`dâÀrƒíÀ’PTTdfÝá?ÿüóСC , È:ÁÑÑ1==œ¶¶ÖÌÌ,''ïŒ) ž°œ„„DSSí]µÿý÷ãǏ3<„ˆˆÈ©S§6nÜH&“ BBB6lHLLŒÿŸµµ5¬ˆ@q`‡¹sç>}ú”FYYY ‰Áv§‡’’ÒÖ­[œŒŒŽÀ:cƌٳgOTTÔÕ«Wmmm=z„wFð2(nì°råʌŒ Ú}Ž=ÊÌ( ,ÐÒÒb`?Nö‘‘ùã?¢¢¢þþûo“!ÿAŒâÀZZZoÞŒ¡ÝGQQQPPðýû÷Ì äëë[XXHû|IIIíÞœûÒ¥Kåå喖–Ô%9²²²®]»†ozð(nl2uêÔ·oßÒîÎä@'OžŒŽŽ²–—˜˜˜¯¯ïùóçËËËW¬XqþüùžžžÀÀ@///Îy¬.Å €M,,,RSSi÷QQQéììüôé3 ÆÅÅÔÕÕ1‡ DEE}}}¯_¿ÞÜÜìèèXSSÓÐаcǎÐÐPŒS€‹Aq`“ÿüç?Ϟ=²ÛÖ­[™ŸŒ‘””ŒŠŠZ·n][[“¡Ø@DDÄÇǧ¢¢¢±±A¯_¿FFFãÜ Š›„ñãǹ†Fsssuu5“éššlÛ¶ÍÝݝ+6™)+++..ŠŸ¬¯¯ŠŠòõõÅ1%žì-`Ÿììì7oÞlÞŒ™v·×¯_'&&9r„ù>}úŽk×.æCan͚5èqwww}}}{{{kkk[[[GGGgg'…B™={¶ŽŽ4Ÿy²ßH»«ºï'a$ÓÓÓóóóÃ$”&Q€‹/ŽŠŠ²ž™>}úÁƒëëëedd˜ÑÙÙ9((è… ŽŽŽL†Â™Li¿Åéakk‹w 쟆o=\–°??ÿرckkk‡ì¹e˖Çc2hhhèßÿÍQ;OX Š[™™™Ñ³xݬY³H$&{E„ØØØãǏ¿~ýšùhÎÅ €­ îÞœKOÏ;v}Ší躺ºœ¹²Ÿÿ>jÔ(Œ³€CAqÀÁªU«ÒÓÓéìŒ}ûvæw›êÇÍÍMHHèØ±c؆Ł@HLLœ6mššššžž^QQòß?» z@&“===¥€€TUU£¢¢š”÷ôô„††*++KJJºžžPï40&¢££µŽŽˆD¢ŠŠf~~~||Œšššˆˆˆžž^ii)Ú-;;[WW—H$N:õÒ¥Khã`‰dß¿gàD꿁@ˆŠŠRUU•––öööŠP(hû€oÁ€Ÿ~Ÿ#rï[Å ¢¢¢üüü_¿~¥§³ŽŽŽššZnn.¶9544DGGcs)))YYYuuuFFFȯPôöö¢¡¡¡oÞŒÉËËë[2?~<777''çǝ!!!4bҐ•••––V__ïàà`llœššzûö톆ê¹NNNAAA)))™™™hã`‰dŽŽŽSŠL™7oÞå˗Þr!55õÁƒ¯_¿~÷î]XXÚ8à[0àg ßç‡.~ë{iii§OŸŠ³sss³©©)+ÒØºuë™3gXyH666CöA€ŠŠ=nmm¡¶Sûššš””” Çèî›è±††Fii)z\[[«€€D;æ` H$jçÁÎUTTŒŒŒüüùsßsKŒ6zþYž×Œ3Ð_Ÿ"""ÊÊÊK—.MMMµ²²òD€O9R\\Œ©ªª¢ÇŸ4>ôŒÈœo=lœ À…B±··¿~ý:ý:€¥¥…ù]Žœœœ›6mš?Ÿœœ=¶‘‡dkk{õêUÚ}„ÿ÷Sšú²o»ˆˆHss3z NGGú[A"‘ØÞÞN=—¯»»›FÌa%Ðïå˗/÷íۗ——'))addD#1Úlll†ì܊‹‹û>ýG$ùøøŽµµ>|HûÄŸo}{{»°°0‚ è»<à[@çg€öˆŽ_bøÖÓó?‚^XUI0\6l𬬀³s{{»…BÁ<žžôôtÌ#ÓFçÌ̀/:fnÔÕÕ?~üHÌa%0à˞žžÌÌLYYYډÑ6fn„„„õõõSSSÉdò°> ‚Œ}û=...ŠÎÜ úœƒ}Ð҄ÎéyÉio=ÜsÀÝ•+Wèì,,,Œf͚3gÎ`ž@8yòäõë×é\}‡ÓØÛÛûûû“H$‰äïïOm÷ôôtss+))ikk{ö왙™ër077/(( “ÉHŸ[_Kl$#‰óæÍ‹---ÍÍÍ577n??¿êêêêêj??¿Õ«W£Ÿƒ}€¥¥KJJ0ùŽ8ô­ÇªJ€áêîî֝4===fffÍÍͬH†B¡ØÚÚÞ»wÁ„ÕÌM{{û† $$$”••=*((ˆ¶wwwGFFjhh‰ÄY³fQ§Š‹9¬úœLII™>}ºˆˆˆŽŽNNNíÄhã홛 wææÔ©SèPžžžhû€oÁ`Ÿððð±cÇòð[Å O[¶l¡ÞIÇoݺ•EÉŽ··[ZZ>xð€EñûaÅoñׯ_+++c–yô'Å m\7+Ë[—¥x²··Ö-„sçÎ%‘Heee¬HFXXøÒ¥KÑÑÑwîÜaE|Öñóó«­­­ššð÷÷777Ç;ÿñ‰VÃ÷­‡â€§™3gôç±Íœ{÷îÙ³‡Eù ÅÇǟ;wîÆ,‚&Nœ8{öì9sæššš„††20NH °OŸõlú™9sæ³gÏæÌ™CgeeeyyùŒŒŒ… ²"!!¡ÄÄDww÷îîn V 9______f" «Ÿ€ó‰~0§xò­‡™Î/_Ÿ<¬S‚ƒƒÃÂÂzzzX”??ÿéÓ§oÞŒyá `(n8SUUýðẶÄÄÄLLL†[ ??LLÌóçÏX7 € žàï·ß~»{÷î°NÙ°aÅ ÚÚÚX”‚ áØ±c¯_¿ŽŠŠbÝ(ÌAqÀŸ££ãŋ‡u ??¿«·õ&GýüùóñãÇY:CpC1RRRœœœõõõ222ôŸeddÿùóç &°.7A<áççÎLJÙ߄•••IIIXEã•••x§ÀnðIÀÿîÝ»ñΐѣGçää̝;wXg͙3'((hÕªU,ʊjîܹ­­­aaaÆÆÆØüY8nÜž®®.LBñ’%K–š««ã[Á'eff&++‹I(ØÀzzzLMM333‡{âŸ}ûf̘abbЬú¹wï^ddd||Œžž8†0î¹p>>ŸÙ³gÿóÏ?Ã=100ðÔ©S­­­¬ÈªŸ%K–ìÛ·ÏÎÎn^:€‹@qà®®®ñññÃ=KHHè÷ß?xð 2€ŠŠf\\œ»»{aa!{F 7N1~üøúúú?~ ÷Ä¥K—~þü¹ŽŽ”YýL^^þ… ÁÁÁ>dψ€a{n$))éǏ...Ã=±¶¶ÖÝÝ=-- “mqèÑÞÞîîîŸdɒuëÖ±gD`æÀAÌÌÌ2228QVVö×_œrå æ) FDD$!!¡®®ÎÓÓ³³³“mã†Å €ƒ ©ššŒ}û–sœœœ›››1Ïj0!00ÐÔÔÔÒÒ²®®ŽmãhƒâÀYÜÜÜΝ;ÇÀ‰üüü{öìÙ³gæ)ÑfddîääôâÅ 6 7΢¡¡QZZJ&“8wöìÙ˜gEÛäɓ“’’ÂÃï_¿Îæ¡?ƒŠ'66VBBÂÒҒs׬Y“––†Õ:Âôëîîîíí=pà†»4† þû8ŽÝµk×;WRRÒÍÍíȑ#ØŠD~~þ°°0---[[[žAqà8£G–ššš`ìt33³·oßa›V¯^}øða77·¬¬,\Àe)'zõêÕÕ«WÃÂÂ;œ¡¡ÁÉÉéÆ‚‚‚Ø&F§ÎÎΰ°°ŠŠŠƒŽ5 —±`æÀ‰f̘ñöíێŽÆN—––öððÀåâJPP0$$ÄÐÐÐÜܜ ³Ì€âÀ¡lmmŸóA33³’’Œ.N¡ ®^œzåÊOOO¶•0.K8…B±¶¶NOOg8î§šòòò8àëëkhhˆo&Œ0sàPBBBÚÚÚϞ=c8î§š.\˜ššúôéS{{ûÿýïtàq0sà\$i×®]gΜa&Ț5kŽŽŽ°ÊŠÕÕÕ!!!bbb{öìÇ;xÌÜ8—ŒŒ|{{{CC3AŽ=ºuëVÙÛRAA!..ÎÆÆfõêÕ§N¢P(Ô/Á}Ç`…÷îÝx烒’’JKK[žp!ÈD¢˜˜Xzz:3A°¥ššhooÿå˗ÀÀÀ–––éÓ§WWW/X° žžØÜܜ@ à Ü fnM__ÿéÓ§}g8`ffVZZúæÍ¬²b@011IKK255uuumnnNII±¶¶îééÁ;;žÜsàt.\èééY³f 3Aš››®_¿.""‚UbX©©©™={vuu5‚ D"ÑÐÐ0)) v§€aPÜ8]gg§¹¹yff&“×kŸ‰±ˆžžÞøñãñÎð2žçÀéøùù?þÜÕÕ¥¬¬ÌL%%¥޵µ©««c•&ЇÃ%$$$$$ÄÅÅߟ}›››‹ ïOž€Aqà~~~˜LÞ râĉƒ2¹ö1ׁåÁÅ €k̙3ç͛7ߟ}c>”°°ðÑ£GœŒŒ`Å<x7nâééÉä>šTjjj‡Æ$€s@qà&†††÷îÝëèèÀ$š]EEº¢ €g@qà2ŽŽŽ.\À*Ú±cÇ:ôéÓ'¬â‚@ $&&N›6MTTTOO¯ššm§P(^^^RRR“&MŠŽŽîÛ?::ZKK‹H$jjjæççÇÇÇ«©©‰ˆˆèééQ¹ÏÎÎÖÕÕ%‰S§N¥.2D&“===¥€€TUU£¢¢à>À žp›äääîînL¢ ÇÄÄxxxŽ··c/)))YYYuuuFFFhãÞ¿_XX˜››‹®žG••••––V__ïàà`llœššzûö톆êéNNNAAA)))™™™hchhhEEś7oòòòÒÓÓÙù=@'X¡À}þüóOIII¬æççŸ;w...«€ÌÖJŸ¡ŠŠFVVA¶¶6iié¶¶6A&MštãÆ©S§"RTT€¥¥…þÌ'$ ]й­­MTTtÀÓ'L˜`nn®ššHKUU5++KCCA·oßN›6mX¿G`…bÀ0sà>... >èŽ`ÁMMÍ?ÿü«€ì‡–&‚‰Dê,TuuµŠŠ zù ôªU«$%%cbb° Ë3&Nœ8{öì9sæššš„††âýAqàbD"qéÒ¥iii˜GÞŸ}ûóçÏoÞŒ‰ydàëë[YYùå˗˜˜ôIr8 7î¶qãÆ³gÏbµ`1ºAAbbâãǏ± `5(nÜMHHÈÜܜºó†øùùãââÐ 0`(n\oíÚµ—/_ŠP(˜G&‰çΝóõõýòå æÁ,Å €ëñóó;;;ÇÆÆ²"žŽŽtdd€««kkk++â0Å €ØØØdee¡Û=bnòäÉ»vírrrB×êp8XÄÀ#²²²Š‹‹·nÝÊ¢ø=:yòdBB{Öä}õêՁØ0ûíØ±cƌxgx7Þann~îÜ9 Å¿sçNBBBBBŸ‹hƒËRÞ±cǎýû÷³.þ²eË,--=<<àÏB87Þ1gΜúúú²²2Ö aaa1þü€€Ö `7ž²gϞœ{÷²tˆµk×Nž<ê87ž2qâD99¹üü|–Žâî¡áåå×§à@pC1€×477;::fdd–töìÙçϟŸ:uŠÕ†fnŒfìØ±ÉÉɬÈÅÅeá…0§™êìì455MKK5j«Ç:þü“'ONœ8χÀ!`æÀƒ7oÞ|ôèQ6ŒµfÍCCCGGǎŽ6 7Þd``PPPPQQÁ†±LMMÝÝÝíììŸÿΆáŽAqàYaaa»víbÏX‹/Þ¹s§]}}={F ŠÏRUU•——¿wï{†›9sæ±cǜœœ>þ̞‚ŠŒ¬­­ÍÊÊ*===»]"RUUåîîŸcǎùóç³gD@?PÜxܕ+Wêëë7mÚĶ;::Œœœg̘áííͶATpY ÀãìììrrrêêêØ6¢°°ð™3gZ[[}}}»ººØ6.Å €÷íÛ·oûöíì‘@ ZXXŒÿžCà²`D ZŒxñ²eËØ|XŽh‘···€€.90BÀ)`€XŒxqMMÍ«W¯p]UU5%%EUUuŊwïÞÅ%F˜¹Œ _¿~uvvNOOÇq“Ë–––ÐÐкººÝ»wO˜0ÚÞÕÕ3:`ŠÀÈßÖÖ¶qãF|Óxÿþ}hhš””ÔöíÛǍ×ÐР®®~åÊößò ïâ0²ôööZZZ;vLII ï\¢¢¢ððð_~ùåÛ·o111ÒÒÒ'Nœ°±±Á;/ž7€§ººÚÇÇ'99™@ à ‚ Hvv¶³³suu5‚ ²²²üñ‡££cFFFXXØøñãñÎcUUU~~~ÖÖÖx'x\ߌ8 †††ÑÑÑž_œBœzõª¡¡=®­­õóóûöíÛžqãx²HJJÂ;Àûài)ÀHäæævïÞœ²²2ŒAÉËË?~Œ¢¢¢ŒŒŒŽŽt[[Ûï¿ÿž••…w^p+˜¹ŒPîîîéééž/œ––† HOOO]]]}}}mmmuuõóçÏñÍ îÅ `„’——·ŽŽ|Øßß¿«« ïD˜â0¢ÉËË{zzîÝ»ïD0À!O¶€;(n#±±qccãœ{÷ðN€ (n9räȑ#GñN„qèŽ @ Îßtuu…„„())ÉÉÉEEEQ»EGGkii‰DMMÍüüüøøx555==œÒÒRŽ[vv¶®®.‘Hœ:uê¥K—ÐF2™ìéé)%%¥ªªE€cAqˆ°°ðáÇ9ä±)Æ ËÍ÷ööRם?tèP^^^ÎÿŽw÷1mÔÀ¯Uƀº±Ò(Ó¡Ý€›¬:ÃPcŠF¡’Á`2ÌâÈtMq’1Œiþ¡PØp‹Ûœ. 1‘- äa0;+]WF‡žmy(¿?nöÇCËÖîàۇ÷ëåî˵÷þÞ÷6>ûÞõÚÜÜÙÙÙÛÛkݲ±±±¶¶vxxx×®]ñññ555MMMƒ!!!aߟ}ô6»wï>tèÐÈÈHuuuCCÝšR©4MOOOkkk]]ÝòöÀøú€[JJJ8ÎÞœ{I¡šÿžäëЊY¬9ÿ€¯_¿ŸŠŠ&::zÞ6:níÚµE\»v-88˜^¥!HQTHHHnn®D"Ö׊D¢ÆÆÆÈÈHŠ¢.^ŒåÄo'úà(ÌÜÜràÀo¿ýöÒ¥K€ƒ0C«Õ®[·na;]ÙPåïïOQ]ÙЫF£‘^®­­mnnÞŽiSxxø¹sçèFNF/ …Â% p7PÜÜÂb±>þøc¹\NÏ^žy7Á§¿_B,×ÔÔ 9r$++‹n|ðÁ¯\¹B/«Õ껉 °€PÜüßý÷ß_PP°ÿ~ÒAœÁãñúûû­«{öìyóÍ7Õjµ^¯wô+B%Igg§ÙlŠfÕLiii …B§Óét:…BÁ`rf¡ž˜#666&&æØ±c€ƒ8ìwމ‹‹³Ö"¹¹¹qqqÏ<óŒX,Þžq£Coõꫯfffr¹ÜÇùå—tc~~Ÿ@ ˆŽŽ~ê©§^|ñE†;ÀÜP `CVVÖk¯œG*€‹ßxÛÝÝ-‘Hœž8åâýπ™JKK•J¥Á` ĵÈårœ^¯Ñh …D"!À676p8œâââìììééiÒY\HhhèæÍ›Ÿxâ ¡PšR©Hǰ Å €m)))J¥’t"“ɇ††>ýôSú“ä.Å €]©©©F£ñ«¯Ÿ"€â`1EEEµµµ?ýôé p§PÜ,†ÍfŸ8q¢  @«Õ’ÎwÅ Àmp8œcǎI¥R7}r1€·Aqp{¡¡¡ùùùÙÙÙx6€ëÃCüîÔgŸ}ŠÓéòóó—a_õõõEEE|>öµœŽZm^^^BBé àÉPÜ8ààÁƒ6lHOO'ìÂe)Œÿþû---MMM€ƒ€](nÀb±ÊÊʎ?ÞÞÞN: ؆ËROMMýàƒÂÃÃIg€ùPÜ8Ã`0dddœùdïÞœÿþû/é,0Šžö‚Ä QIDAT' …Â÷Þ{/##Ãh4’Îÿ‡âÀy=öXnnnzzºÉd"nÁ=7w«­­­žžø‹/Ÿðõõ%š{Ip{O>ù€ÉdÊÌÌ<}úŽé8Þ—¥ðÜsÏíÙ³'33sjjŠto‡â€/ŒðBzz:êâPÜ0楗^JJJÊÊÊ2›Í€³x/7Lzùå—srrvìØqãÆ ÒYŒ>-ÀŒŸŸŸ·Þz«¢¢"$$„t¯ƒ™æEEE?~üõ×_ïèè Àë`æ`©ÜŒyóÀ1112™ŒÅb‘Žà-0s°TV­ZuêÔ).—›””444D:€·ÀÌ À’£oÁyûí··nÝJ: €çCq°ŒFã»ïŸ;::ZTTD:€'Cq°|º»»óòò¥R)îÂX"žç`ùlÜž±ŸŸ~rr2))©««kö, ©T37 ÞŒyóðáÃB¡pff&""B¡PìÛ·t4·‡âÆ«=zôüùó€S,‰ÊÊJҐ‘‘a2™H§p …"66–t gšÕj¥R¹zõj±X,“ÉØl¶\.ÏÏϧ(ª«««°°tÀ%áŸãîâ^Ò€€ .xF0ÏΝ;IG`†ÙlöÈbVUUÕàࠛþ² …'OžìêêJKK¥(ª€€Äl6+•Ê˗/''''''“ÎÈ0·/p(n›šš¡— CYYÙõëןþy²©Ün( ¬¶¶vŊ|>íÚµ\.wllìóÏ?¯šš  À]aæ€0•J¥R©Ìfóðð°^¯ÒjµÍÍÍøü€sPÜž___>ŸÏçóéU.—K6€ûÂe)`†õqdmmmB¡õ²©`Ù`¬Àu ž†åå啗—ÏÌÌà)@Š`XOOϳÏ>K:…÷ ̃SŒŠ`ØèèèŠ+H§ï…âÃb±Îœ9°e˖ÞÞÞy?¥ÿœ÷ŸÅßÿ}ǎAAAIIIƒn7›Í999AAA"‘šŒŒÜúªïŸûN,ûûû?òÈ#gϞ]–žÖÖÖ655åÄ Y,։'›Í^xüY,ÖÑ£G7lØàïïýý÷ߟ:u*<<ÜÏÏo˖-ôf6žœÑ±§ŒŒ\$ñxŒ7Þxcbb‚nŽ7ô6÷h±XT*UXX—ËÍÊÊs‾£ÉÉÉ~ø©ÏF¹Î)@ ŠpXuuuccãõë×·mÛ6ï{pèûlÞp“œœŒÿ~­V«Ñhz行Œ<º]¥Ri4šžžžÖÖÖºº:ëö»wï>tèÐÈÈHuuuCCÃÒ÷‰°‰‰‰íÛ·‡……%&&¶ŽŽ8z»RCCÏ?þh±XlÿÆÆÆÚÚÚááá]»vÅÇÇ×ÔÔ455 †„„ëðÙ<àöFǞššš–––ß~ûíÒ¥KEEEt£œ¡·¹Çâââ .477ÿñÇ“““÷¥Õjããã×­[—ššÚÑÑq÷oè"§13àÅRRR} EQ×®]£—ÇÆÆüüü¬íóæ-[ýóÏ?|>Ÿ^ …ýýýôr__Ÿu{@PZZú×_9æD¿È2›ÍÖ†‡‡'''·¶¶ÞIG(ŠúóÏ?g¯Îû©N§£—鉛Ãgó€Û{1úúúèåÞÞ^‘HŽp›ÙCos‘‘‘ô²^¯øá‡Ù£Ueeeeeålé²Ôjupp°õ “J¥GŽq®_.rJØãã®_œéÕvîÜùõ×_;ôkÎ9c]]ž0{ù—_~9xð`ggçßÿMQÔ=÷ÜC_‚ñóóõõõ¥(Êd2Ñÿ°Rõ믿*•ÊÖÖV.—[RR²mÛ6GûÅb±Üèh|>¿ªªJ«ÕZ[î»ï>‡óÍ7ß,þZº§³/:Ø ÅWmp{£c/†Ñh\¹r%œñš5kŒF#eèmîÑßߟ~ÍfOOOßöèUUUUTTžõSaBCCϜ9£×ë­-'  °°0++ËÑws‘Sžªª*Š¢<ï;³Àµ,{9.ř›« f/‹D¢Ó§OßžqÃú:tûâÿŽX, ÁÁÁކtߙ›Õ«W¯_¿>99¹¥¥Åb±ÜáÌÍìUú—“œŸ.Ÿ:ï€;:ssñâEëÆÖ™{CosW®\Yd/6yÀL€uæ&000$$$--­££Ãé~¹È)aŒž>ÜsËalllÕªUF*•ZÛÓÒÒ …N§Óét …ÂÚ.‘H:;;Íf3åde³ÙSSSÛ·o¯®®š¬¬|ú駝ë8ÇëïïwôU6žœÑ±G.—_œzõêÕ«r¹ü•W^¡í œÍ=æääH¥ÒþþþñññŸþ911Ñюž)ú†)))çΝÓh4gϞ‹ÅLœ9ÁS€ÒՐŽl37>>>!!!¥¥¥Öv£Ñ˜œf͚°°°>úÈÇLJn¯®®~ôÑGýüü6mÚÔÜÜìhH·›¹±Ç‰™›?ü000Ðæp,²jó€Û{1ÊÊÊè:åää˜L&ºÝÞÐÛÜãôôtiiidd€¿¿ÿã?^WWwÛîÏxîLS37€N { pyroute2-0.4.21/docs/html/_images/inheritance-f66e473c49f615627cbf040680338f04db639d10.png0000664000175000017500000014276013152333364026766 0ustar peetpeet00000000000000‰PNG  IHDR–Ï!äùbKGDÿÿÿ œ§“ IDATxœìÝy\MùÿðW-Hi_I( š‘˜aƚ±M$Í [rÂD)‘ÊÎÏŸïKeÌL“¥ÂwD3H–”¥”ŽZÚ»ŸßŸoW·Õ­s«÷óñžÝsî9Ÿ×¹÷–Ïû|Î"Ãc „B!„4 ²| „B!„Ô*!„BiBš „B!€ ¡€B!„&€9ß!„T-,, ˖-ƒ¡¡!ßQÈgzùò%nÜžÁwŒÍÑÑYYY•¥ý˜â<|øwïÞå;‘bTBHŸŸ9sæ`òäÉ|G!ŸÉÎΎï ^AA>Œ–-[òE*ÑwŒT…JgB!„Bš*!„BiBš „B!€ ¡€B/dddštû„”EßGRŸš „B!€ ¡€B©²{eddž‡––‘‘!vŸ¬¬,ŽŽŽ`oo€€€Zµ' 999X²d :wî EEEèëëÃÖÖQQQm§6homÃÓБ$*!€‰`Œ1†Û·o#//óæÍ;¿žžwî܁®®.ìííyJûñR† EVV"""0fÌ,X°€·L€ñhh¿„H„ÒÄèêêbÓŠMøý÷ßÅΗ••…ŽŽ<<}Š¢¢"¬X±¢Òm%’uêÔ),X° uÞVCø} D¢!„©Ìöïß_£eÊþ‰/ûsrr2›0a›0a‚ÈüO***ìþýû®?''‡ééé‰m£”±±1»wï^…ùRRRžç>|` "ë÷ðð`æææL^^ž±… ²ììlî5FFF,66–{~ïÞ=Ö±cÇj·¿yófŠ££Ãþþûo‘y&&&ìáÇÜóÔÔTfhhXé¶V×øñãkœlS"ŠªªÊºvíÊV¯^Í}_&MšÄòóókŽŸ†þûPô#U‘aŒv_BˆŽ Aff&&Ož\íedddžœe÷njhh`ðàÁØžq#ÔÕÕ˜ތyƒÀÀ@Üžqýõ ::nnnˆ‰‰AVV Y³f(..»PPP@VVäåå+ÍWÙ4àã^Øžž8øùù!==!!!bןŸŸnÄ ªöŒŒ`gg'2jŠŠŠåFdeeQRRRiÎê°±±¡cÉ«áèÑ£8{ö,÷\FFÐÒ҂’’ÂÃÃѲeËj¯¯1ý>TÅÎÎǏ¯ñr€éhÎwBªk×®]žuës+e„B!Ÿÿþ{êÐ45éHš««ÃÍÍ šššÜŽ &`Ŋ8yò$”••ñöí[šªªróÅýnêëë#>>ݺuû¬ì²²²033C@@ žé:::HHH@×®]ñññÐÕÕ­vûW¯^Åwß},Zވ›n``€K—.¡}ûöb—£¿Cõ¯yóæhÞŒ9kÔñ¯HCþ} äsQ@Œšš(lÛ¶ |G!eÄÇÇcïÞœ|ÇhÒÓӁ¢M›6ŒfÉÈÈ@`` ÌÍ͹i>|@›6m €€„çϟcñâÅ"Ëš©©!..ŠŠŠÜŽ)SŠÀÅÅ»w¢"<==+<÷S–––˜;w.¬¬¬ §§‡äädøúú¢_¿~Ükìíí!žï @ )F«j_OOW¯^Å÷ߏââbînggg8::"((†††ˆÅš5kpþüù ·µºäää`kk[ã嚚šš(ÈÊÊB]]zzzptt„œœ=ÚŽiS£‘0I†ßB$‰N&„)ñèÑ#Lš4 &&&èÖ­fϞ¿ÿþB¡°^Ú/{íóΝ;ãßÿÅ¡C‡žù»w»;Z·nïŸûýû÷YÞÝÝVVV"{>-Z+++ôïß={öD÷îÝ«ÇßßÁÁÁøöÛo¡¬¬Œ¢yóæ"™–/_SSSXXXÀÂÂfffX¶lYÚ×ÑÑÁ•+WpðàA¬^œàââ‚Q£FÁÆÆêêê˜={6ŠOŸ^é¶É2dŒœœq÷î]DGGcÖ¬YõZKÛï!’Dçcʔ)4 …JGŒŒŒøŽÒàݺu ?üðÞŒyÃMSUU…’’TUUáääDWièøìÏ7yòdìÚµK"‡5Fô#U¡C€!D‚ ™™‰¬¬,îßÒGÙçe¯U_ªY³fhÖ¬™ÈŽæÍ›C]]C‡…¢¢b}m!„FŒ B#//O€ó^Ñ#??_d¹üü|ŽmÛ***PQQŽŽ޵µaddÄMk×®Ø=—………èر#š7o===|ñÅðööF·nÝž«B!Ÿ‹ B$ès. Øڗ6µíÄ¡uëÖ\‡œì£lG^KK ²²’;•ªE‹PQQ££#”••%¶nB!€„©VÛN|II ”””ªìÄkhh yséùSx÷î]Ÿ#Biä€ç=B$¬ì•Jo óÝwßÁÏÏzzzÕ^‡$÷šçääÀÇǧOŸFRRÚµkKKKžºº¢oߟk§6êzô Šùқ0‰ëÀډ¯èB!„”GiÔJ;ŽB¡©©©ð÷÷‡œœ="""xÉcgg„††ÂÀÀ)))ˆŠŠÂ‚ póæM^2IBzz:/^Œ¬¬,ää䔛ߌysššš@UU•뎫ªª¢}ûöèÑ£7Nr%„Bê€I••…ŽŽ<<< ­­ÍMüø1–.]ŠË—/£€€ßÿ=vîÜ 555n¡ôßÒb¢žžžžžØ¿? áááÙ³gsëEQQV¬XQé¶VW˖-Ñ¡CêüB! ÝŒ45œ˜ž;tªšš ""fffb—yûö-ÌÌÌðòåKnŸþŠtêÔ gϞåöêÚfJJ ŽŽŽ¹¹¹PSSCnn.·þõë×ã… xôèttt`ccƒå˗sçŽ;"88]»vÜ¿cƌA|||µÚߌy3ŒœœqîÜ9|õÕW܎ô€§§‹ÜµøXÌ)**ò~n )/77!!!|ÇhÐ\]]‘””„’’€¥¥¡E‹PWWç;–Ôxóæ ._ŸÌw "Åš Fm €²_ï7oÞ 007nÜÀ_ýˆŽŽ†››bbb••àã͘Š‹‹Å®••yyù*Û¬hðqT"..~~~HOOç:Ÿ®???***܈AUíÁÎÎNdÔˍ:ÈÊÊ¢€€€ÒœU¡€_«W¯ÆªU«  ¹i222èСŽ9‚Þœ{ó˜ŽÉÊÏÏÇõë×qúôi\¿~ÙÙÙHKKCdd$zôèÁwîDùꫯ°xñbž“Ò1B–››[í×yšªª²Q£F±øøxî5¡¡¡¬K—.LNNް   Vö×bÆ ¬mÛ¶"Ó ÙÒ¥K™žžÓÖÖfÛ¶miS\ŽRQQQlüøñLWW—ÉË˳:°9s氌Œ î5yyyÌÅŅijj2MMMæââÂòòòjÜ~JJ 355ežžžŒ1ÆJJJXPP311aŠŠŠÌ‚?ŸÒm­Ž'Ož°¥K—ÖhRs÷ïßgnnnìûï¿g>>>,%%…›·råJŠ©©ÉþøãR·²²²˜±±1÷7œS§N,;;›ïX„4Hti0jz©t@݉‹‹Ãñãǃ=zàçŸF§NÊœîýû÷ÈÍͅ††) ©{>„««+æÎ ggg!88XäB„ê£ûBˆINNÆ©S§pýúuhkkcÜžqXµjU¥ËŽjÕ ­ZµªŸ€„Ô³3gÎàØ±c8zô(TTTðêÕ+Œ}û–:ÿ„|*!„gïÞœÃɓ'uuuØÛÛÃÅŲ²tšiÚñðáC9r͛ì²Ìœ9“çT„4|TBcˆŒŒÄþýûñîÝ;Œ?GåîøLHSƃ››޵µ±mÛ6ŸãÒèP@!õèÕ«W8xð """`aaò‹©ÁÃüùóannŽ)SŠð‡F‰ B©c%%%žxñ":eee888ÀÍ͍ïX„HÆ\\\`ii‰‰'ò‡F‹ B©#©©©Ø¿?®\¹‚`Û¶mPUUå;!RkÕªUèÙ³'uþ ©cTBˆ …B„……aÿþý——ÇôéÓio?!Õ°{÷n4kÖ ÓŠMã; !€AÙ³gäääêµÍ>@(¢uëÖõÚnC‘––Æw©ðþý{ìÛ·/^Ä Aƒ°iÓ&ÚÛOH5]»v 7oÞÄ®]»øŽBH“@7# FBBꌝ‚‚Ü¿—/_Æ£GðêÕ+,]º}ûö­ó¶*SSSèêêòƒ‰‰‰Ø±cŒ/¿ü7n„ššß±i°/^ '''êüRÏh€ÿzÿþ=ÌÌÌðâÅ €––ÂÃÃajjÊs2·ׯ_cߟ}ˆŠŠÂžqã`oo_ïç¢ÒØ;v Ož<‡‡ßQirš À˗/áêêŠnݺaǎÈÏχœœœøŽFxtçÎlÚŽ B¡³gÏFïÞœùŽDH£˜˜ˆ… âäɓ••å;!MDšŒ“'OâÀ@ǎ‘˜˜ˆwïÞQç¿ ûóÏ?±}ûvc͚5MögBê‚P(Ä/¿ü‚M›6Q矞Ði²²³³±páBtêÔ ‹-âþ#bŒ1Fÿ151B¡¡¡¡Øµkzöì @ŸcÒèøùùAOOööö|G!€É¢Ò$ýþûÌÍÍEæÉÈÈ@FF†§d€Ÿàøñã8v솊ãǏӥ ©#qqqˆ‰‰Á¢E‹øŽBH“FiRrssááá-ZàüùóhÑ¢ߑOÞŸ}‹œ{÷âÏ?ÿÄøñãL×ï'€cá…te5B€€Éžyó&V®\‰å˗ã›oŸá;áIjj*¶oߎèèh̚5 !!!|G"€Ið÷÷nj3 ¡¡ÁwBš<:€4zEEEðòòÂóçϱiÓ&ŽjՊïH„÷ï߇¿¿?dddàêêŠ/Ÿø¢NÚyðà–,Y‚–-[ÖÉú ©ˆŠŠ&6mÚÄw ±’““1oÞ<œ>}ºÆËÚÙفº*Ÿ¯  k×®E×®]ùŽB€F-.. ,ÀŒyó°jÕ*Ÿãܹs6l€ššV­Zƒ:m/55ÖÖÖt)RïìììøŽP¡%K–ÀÛÛ»Ö˟8qB‚išŠ€€dggóƒH *H£uàÀœ?{ö쁖–ßqH=‹‰‰APP”••áççŸ#Ò$EDDÀÐÐ]ºtá; !俚 Î›7o0wî\XYYáÔ©StEŸ&&22[·n…††Ö®]KÅ!<***ÂÚµkqêÔ)Ÿ£BÊ €4*þù'Ö¯_õë××Ù1ÞD:EFFÂßߝ;wƖ-[èþ„Hœ{÷bÒ€IPTTä; !€ *H£ŸŸ_ýùùùŠË{6!aaa D¯^œ°gÏŽmۖïH„………ÆùóçùŽBù€Á‹…»»;,X€ð‡ÔÆBBB°cÇ|óÍ78~ü8íalÄdddju˜Ú.' |¶--¶oߎéÓ§7ø»ªÓgI#*HƒÅî]»‰ýû÷CUU•ïH€Ž1ÆpæÌìÙ³?üðNŸ>M—Û¬cÒÖù‘¶,v=5ýUô™V–§¢e*ËX7ndQQQ5Z†4^T!11‘5Š]Œx‘ï(€ŽEDD°±cDz•+W²œœŸãÔXm mmmöèÑ£*_·aÃ6pà@–À233ÙĉÙüùó¹ùؘ1cXbb"{ÿþ=ûõ×_Yߟ}kŽüèÑ£E:be; kÖ¬a `ñññ,%%…9;;—{Ýå˗™……+,,»ŽêdøTe@eÛ[úÚàà`Š®®ÎΝ;Çͳ¶¶fãǏg‘‘‘,77·ü›Íóðð`ƒ b/_Ÿd/_Ÿdß}÷[±bEÞqmWö|Î~9i)FŒ!R(U‡®®.ÓÔÔdææælãÆ,33“›'‰ {÷î,<<œåææ²ììl6wî\6cÆ nŸ¶¶6;uêËËËcqqqÌÞÞŸÜzjû=÷™V•GÜ2•e¬*HYT©wêÔ)fccSáÞ!Ò8DDD°qãÆ±•+WV¹gVšÕŠhÞŒy¹Îƒž=€&&&ìáÇÜóÔÔTfhh(²LJJ ÷üÇLAA¡FË?{ö¬\ŽRÆÆÆ"{Ø+zÝ… EF4jº •­ûӟ+Û^lóæÍLGG‡ýý÷ß"ëÌÉÉaÌÜܜÉËË3###¶páB‘ïž‘‘‹åžß»wuìØ‘{^ÕûQQەœŸSØÚÚ²§OŸòú8zô(›?~—366æŸïrrrLOOYZZ²}ûö±±cÇÖêýšìœÌÉÉazzzÜs}}}Ä^Œx!v=Ÿó=ªÎgúiqËT–±:š eÉ0F—S Ò)//˗/G›6màááÑà/%GċŒŒÄ† УGžºº¢M›6|Gú,áááxüø1œœœªœŒ¶¶6®^œŠÎ;—›Wöª7ŠŠŠÈËË™/++‹’’’r¯­íòB¡PäIJË+(( ++Kì9e_WXXˆ>}ú 00ýúõ“è6TôsE¯522‚ŒœœËe.% ???€§§#$$DìöæççCEE…Ë_ÕûQQەœŸs•£±cÇ¢S§NµZVRBBBðý÷ß×ø²Œ'Nœ@bb"÷\FFêêêPVVFûöíñÇÔ8KÙ÷2::nnnˆ‰‰AVV Y³f(..ܹs«W¯FDDTUUkkkn=Ÿó=÷™V•GÜ2•e¬Ž€€|ýõ×°²²ªö2€«÷’ƒjˆeÇg|G!uäΝ;lüøñlÙ²e,++‹ï8S›æîî.v^Ù?Ó]ºta‰‰‰®GܟtI.ß©S§j0ÆØýû÷™©©)ËÉÉ©Q†Òs*ZwE?W4ÿåË—ÌØØ˜­[·®Â6Keee±Ö­[sÏ«šêýššíÊÞqÛ_]|”‘‘Á&Ož\«e;uêÄ0uuuÖ¥Kæââž|@›6m €€„çϟÃÑÑQdþ˜1cƒ‚‚å/Ãù9ß#qŸiUyÄ-SUFBj„ï „R999lʔ), € …BŸã {ñâsrrböööìéÓ§|Ç©3µ`Œ±çϟ³iÓŠ1&''Ç ™ »~ý:÷š’’ÄLLL˜¢¢"³°°`çÏŸçæ‹û“^vÚç._XXȖ.]Êôôô˜¶¶¶ÈvŠ[V(²¡C‡Ö(Æ XÛ¶m«} Êò–ý9%%…™šš2OOOÆcQQQlüøñLWW—ÉË˳:°9s氌Œ n™ŒŒ<æââÂ455™ŠŠ&sqqayyy5~?>m»²÷@ÜöWß##GŽdEEE_¯$FBCCY—.]˜œœ300`AAA"óϜ9ÃÌÍ͙‚‚ëÑ£ »žÚ|Ä}ŠUå·Le«ƒFHYt‘ ÿùϰjÕ*øøøÀÜܜï8D‚ÒÓÓ±aÃ$''cÙ²eèÒ¥ ߑêTmÎ Dìììpüøq^Ú~òä ¶mÛ‰¯›ÏíjLªÚ=b IDATèRVsŸ²yófܹs§N‚’’ßqˆ„Œ{÷[·nÅßÿ¥K—¢W¯^|G"„Ô‘C‡áçŸæ;!€šè›ŒŒ<̜9>|Àž={šóßHäææ"00“&MBïÞœqúôiêüÒÈÅÄÄ gϞ|Ç „T„?ÆØ±c1uêTž¹¹ñ‡H@qq1vìØñãÇÃÄÄçΝÀøŽE©c/^Œ€¡¡!”JHB‡‘zwîÜ9ìß¿{÷Š&ßqˆ„……! £FÂ… èž „4!ÁÁÁ9r$ß1!5@©7ÅÅÅðððc §NB³fÍøŽD>Ӎ7°nÝ:XZZâôéÓhÙ²%ߑ!õìòå˘1cß1!5@©ÉÉÉpvv†³³sî\H€ÓƒàççMMMìÛ·ÊÊÊ|G"„ð ??rrrTüÒÀP@êÜÕ«W±nÝ:lÞŒ:tà;ù /_Ÿ„¯¯/„B!Œœœ¡­­Íw$Bþþûoôîݛ¢€ÔÆÖ­[‡‡âÔ©SPPPà;©¥ÌÌL¬[· ðòòB§NøŽ$µdddðÇ ''GâëNOOÇÇ+l·GPTT”x»€ryyyHII¡¡!¯‡6ÖÅw®*×®]ÃðáÃ뎍§OŸÂ××·NÛh "##©X#º©999˜5kFŒ‰'ò‡ÔR~~>‚‚‚pãÆ ,Y²_ý5ߑ€^aa!îÝ»W'뎈ˆ€»»; DŠËÉÉÁÌÌ [¶l¡C1xðîÝ;ãŸþA‹-`aa+++hhhÔkUUÕzeµµµÅ±cÇêŽðyðàòòòêlýM…ŒŒ Ÿøâ ÈÉÉñ…H*ˆÄ=yòsç΅——,,,øŽCj)88Û¶mÃÔ©SakkËw‚'ÒwéÒ Ü4uuuØØØ`ëÖ­tõ%)——‡šš(„……áÞœ{066ÆÈ‘#ñí·ß¢E‹|ǓšââbüôÓO8qâßQ!5D‡‰úí·ß°mÛ68p€.ñÙ@ýý÷ßðòò‚••Ν;×è:- Ñ»wïpâÄ £cǎ\ ¥¥…õë×ÓX¥ˆ‚‚ „Aƒ†Ý»w£žžƒÆðáá§§ÇsÒÏ÷àÁtë֍Z "¥Çû?{ö AAA4ÄØœxñžžžhÕªV­Z…¶mÛò©É»}û6þïÿþ¯_¿Æ˜1c`kk‹˜˜ 6 jjj†™™ß1I5åææâúõëƓ'OМ{w 4ýû÷o3÷íÛ555Œ1‚ï(„¢€|¶üü|̝;}úôÁôéÓùŽCj(++ ëÖ­Crr2V­Z###Ÿ#5iÙÙÙØ·oþüóOXYYaʔ)ÐÕÕåæ3Æ àééIEZW::pùòeaȐ!øá‡D>oi&°xñâ“—ò?TÏòòåK̙3K—.…¥¥%ßqH aïÞœ†»»;úöíËw€&íÑ£GØŸ};ž={†Ÿ~ú 666t³Œ&€!ŽØØØà̙3|Ç „Ô€Ö®]»lߟ|Ç!5Œ€€888`Ò€I‘‘á;R“T\\Œ3gÎàèÑ£èÔ©œœœh†ø8:ŒëׯCNNß~û­T0Æ`kk‹S§Nñ…R TZÙ¹s'nÞŒ‰-[¶ÐõýèèhxyyÁÒÒ®®®tÉHžŒÿ{öìÁï¿ÿüôÓOô{D*ôáć‡#$$/^Œ€¹¹9ï£)))X»v-‚‚‚xiŸòyš 5RPP€yóæÁÈÈnnn|Ç!Ք”””””ÀÓÓêêê|Gj’ÒÒÒ°uëVÄÄÄ`âĉt˜©•Òс7n yóæøöÛo1bÄèèèÔ[†k×®áßÿ…‹‹KœµI‘*Hµœzõ 3gÎÄ… 1`ÀŸãjxÿþ=Ö¯_ÀÛÛÆÆÆ|Gj’ž?___Œ}û...t7N"1¥£/^DJJ LLL0hÐ 0͛×ݕŸwïÞ X[[×Y„ºC©–;wî`Ù²eضmÚ·oÏwR…ââbìÙ³ÁÁÁXŽhŸýö[Ÿ#5I/_Ÿ„¿¿?222°téR˜˜˜ð‰4r±±± ALL š5k†þýûcäȑÐÖ֖h;îîî˜1cíT €¢€Té̙38|ø0öìÙeeå:mk˖-xÿþ}¶Ñüõ×_PSSC÷îÝy?Á·]»v˜1c¯ê[ZZe˖ÁÜܜïH€ zÿþ=._ŸŒ‹/âÕ«W055ň#`eeõÙw¶··Ç€ö E„ÊQ@*ˆçϟÃÏϯ^ŽU6lŒŒŒêŒRV­Z…àà`ŸcԋÌÌLx{{ãõë×pww§›t©R::ðÏ?ÿ@VVýû÷ÇšQ£ ¥¥UãuÙÚÚâäɓu’RêîAÒ `öìÙèÙ³'üýýë­]eeeôêÕ«ÞÚ#uOQQ‘ïu®ôž !!!pss£{*©dffÆ¥™™™øë¯¿àé鉀€$tíÚUb£„éG)'==3g΄‹‹ Ÿûî;Ÿã"ÕN:…Ý»wcÖ¬Yžpáßq©UUUØÚÚÂÖÖÀÿF¶nÝ àãhìȑ#¡¢¢RnY¡PHE! DÄœ{÷°`ÁlÚŽ ]ºtá;!R+>>îîîèÓ§.\ž@ÇB“­ìè@FFÂÃñlÙ2Œ|ùœzõÂȑ#ѳgOÈÈÈ -- <'&„|*çÒ¥KØœ{7Nœ8¶mÛò‡©TPP† ‚U«VáöíÛôw…)D@•€‰'ÂËË ƒæ;ŽÄ0ÆÀÃíÛ·‘——W®°)ÿèÑ#ôìÙÜ<;;;ddd 44YYYˆˆˆÀ˜1c°`Á‚úތjSTTDHHˆÈŽóçÏCII‰§DËàææ†={öÀÚښï8„H…ììl±…°ššlmm±gÏüöÛo9r$ÂÂÂ0nÜ8L™2'OžDNN‰ !å0ÒäÜžqƒ1‚¥ŠŠò¥œñãÇ×èõe¿ÂŸ~SSSY»ví*œÿöí[Š  À=WPP`ÙÙٕ¶—ŸŸÏÓÒÒbšššL °üü|n~QQóðð`LKK‹mÙ²ElûÑÑÑLGG‡0Æ+))a«W¯fíÛ·g***lêÔ©ìýû÷Üree×wâÄ Ö·o_‘Œ–––ìäɓbß'''¶gÏ‘×ïÞœ›9;;—Ë€8p€uíڕ)**2KKKvïÞœJßqjú™Jƒüü|6kÖ,¶nÝ:& ùŽCˆTY±b‹‹‹«Ñ2iiiìĉlêÔ©lĈÌÇLJEGG—ûýÊÈÈ`fffìîÝ»’ŒLù41§OŸÆæÍ›qüøqhjjò§N•î#Nff&Ö­[‡/Ÿø‚›6`À̜9QQQÈËË»œ——bccqëÖ-DGGãîÝ»ðööææûúú"""ááሉ‰Áýû÷Ë­#$$ÖÖÖØºu+ W®\Axx8ž>}Š¢¢"¬X±ÀÿËaÿœ(kìØ±HKKÃõë×W®\AFFlllÄæ Ÿ}ûpâÄ À‰'°ÿ~Š}ý™3gŠŽŽ4X[[ÃÉÉIìë“ôôtØÛÛcüøñXŽhØHÈ'rssk|ƒ?uuuntàܹs4h‚ƒƒ1räHntàíÛ·Ɠ'O0tèP,[¶ B¡°Ž¶‚ŠM†}Ú£ V`` ž={† 6HíM\ìììpüøñj¿ŸìI«e~õêwèÎÑ£G¹ùeikkãúõëhߟ=àíÛ·X¿~=.\ž€GAGG666XŸ|9wÉ»Ž;"88]»vÜ¿cƌA||< S§N8{ö,ºuë&6ëæÍ›áíísçÎ᫯Ÿâ晚šâܹsÜœ^¿~Þœ{ãÙ³gå¶íÓmß¹s'BCCqþüy 6 cǎ…££c…ïÍëׯѧOqãW ~ºLJJ ŽŽŽ|üO_MM ¹¹¹Õüt>ªégʧÒû`ÁÄĄï8sþüyÜžqƒïŽ‹‹ tuuùŽQïæÌ™ƒU«VA]]]"ë{õê~ûí7„‡‡#%%—/_ðñÇ®]»âܹsÐÕÕÅÚµké¢Zrvv†¡¡!ß1êĵk×pñâEŸcHµµkזëÑU€š€ââbüòË/011ÁƍùŽS§J¿àS§NEçΝ¹é¹¹¹ˆŽŽ†¥¥%V¯^šš(I¬ÝŠbߟ}HJJjŽÀo¿ýkkkèëëóE* 0ÆšhjÞœ{‡éÓ§ÃÁÁ?üðßqê\uŽddd`llŒÃ‡£wïÞ4hZ·n-òYYY˜™™! Üt$$$p#ñññ"}}}ÄÇNj€«W¯â»ïŸƒŠŠ -ZÄM700À¥K—žbD\抎lÙóæÍÃäɓ±fÍŽlÙ²Òíÿ÷ß±gÏœ9ssçÎÅèÑ£+-Zš€¥¥áرchÖ¬ßqꄑ‘€ªªÊwÞäææBAAAâëý矐‘‘MMM4kÖ ²²²••E³fͰxñb˜››‹ì€!ÕÓ®];Ÿ#Ô9}}}únT U«Vb§ÓÿXrr2&L˜€%K–4‰ÎM _¿~8räÀÒÒ‡BBB ‘˜˜wwwôë׏[ÆÞށIIIHJJ‚@ €œœ=7ʔ)pqqABBRSS1{öl‘6õôôpõêUìÞœk×®åŠ;;;ÃÑÑqqqÈÍÍÅ­[·0zôhnŸššâââ*ÜwwwÁÍÍ­Òmþðá&Mš„C‡áÇĎ;`ggך÷ìW†1†_~ù­[·†¿¿£íü"IB¡°NŠI„……áÚµkžsç?~Œçϟ#!!iiiMºè"DÒšh€þý÷_̜9Û·oG=øŽ#µ±sçNÏ Æ·ß~ eee 8͛7Ç¡C‡ž×/_ŸŠŠŠ°°°€……ÌḬ̀lÙ2nþ¢E‹`ee…þýû£gϞèÞœ{¹6uttpåÊ!uöìÙpqqAÿþý#FŒÀĉ1gΜÏZoe„B!.]º„›7o"..)))5> .”””ÀÉÉ æææuºý„êQTTD¯^œÐ¹sghjjÖÉ(!ä#:š:wî<ˆ£G¢M›6|Ç©Seù©êðqó‡ ‚!C†¬¬¬`eeUé:äååTáqšrrrðòò‚——W¥íkiiáÁƒÜsYYYžžžÀÅÅEìzçϟùóçW¹=Í/ýyÿþýå^÷Ë/¿TºLMڬȫW¯ðàÁäää ;;999ÊÊÊhÛ¶mµþ­­ÂÂBL›6 cǎŏ?þXëõÒѕ±iøšhd‚‚‚€'NÐá D*ÈÊÊbÚŽiÕ~}^^²²²žG~~>òòòðìÙ3‘é¥óÊbŒAAA ——‡ŠŠJ¹‡ŒŒJ mmm©Ùúöí[ŽnÝZjòB‘^T4˜5k†*rB*!MQi¡¢¢"RDdeeÁÁÁ§N¹œ8⊇¬¬,$$$ˆŒJˆ…(..F«V­Ä ŸhÞ\2†'L˜€øøxLš4 óæÍû¬C€>œD)MMM :þþþܕEÊÎo×®,--±qãFtêÔ ““œ>}IIIÜk\]]Ñ·oßZg”=hÜddd0lØ0\Œx±\a\Ñ}R>•@€ÈÈH@¿~ýÈ]q†1wwwìÚµ 0kÖ,x{{sí]Ÿ|ŸŸŸžqã”””0|øpøúúŠ\™§l6q9ªšOø!##ƒŽŽ4tëÖ /^Œ¹ߏ?þˆ³gÏrÏóóóa``€ØØX‰Ý?ãsQÐÀeeeaêÔ©øå—_ž; !¢^¿~iÓŠ! €ë˜VF£âc*[DTv“ŒŒL…‡0•}Žk׎ûO'##Ož<§§'vïÞ øúúÂÜÜŒVÛði&àã•Å~ùå̛7‡.7?##pppàîNmgg„††ÂÀÀ)))ˆŠŠÂ‚ póæÍÏÎFHeTTT°eË̝;·VËOœ8ßÿ=<X·n&NœÈ}¿wî܉°°0ÜŸ}0nÜ8tìØ3f̬\¹ .Ä¡C‡  ±bÅ 888p÷– ¶Ø.«ªù„?êêê°ŽŽÄ… `kk ÈÌÌą žž555ÀÙ³gÑ·o_©éüi°Ù AƒØ?ÿüÃw‰?~<߈„ñý™>þœ <˜ÅÇÇóš£&rssYJJ ‹‹‹c7oÞd—.]bǏg;vì`>>>lɒ%ÌÙٙýôÓOÌÖ֖ÙÚÚ²:0"uuufjjÊÌJJJªÝ~Ùÿ>ýo"55•µk×®Âùoߟe Üs–]i{ùùùL 0---Š©©ÉËÏÏçæ1f``ÀŽŽŽØ–-[ĶÍtttX@@cŒ±’’¶zõjÖŸ}{Š¢¢ÂŠNÊÞ¿Ï-WöQSk×®e·oß®ñr_¿Ó5mËÊÊb;wf<(7OÜϟRTTdoߟåžçää0EEEîyŸ>}ØÅ‹¹ç/^d}ûö­p}oߟemÚŽ©0oejÛe `‘‘‘µZVZäååUøwÄÝݝ=}úŽžýïó8sæ 1b7=88˜`çÏŸçŠ 2„;w®Þ32ÆØ„ Äþý§€êÞœ{pssÃÞœ{¡§§ÇwB€ÒÓ§O1gÎìÝ»·Aݱµt¢ì˜«Röæs-[¶„ŠŠ 4551|øpÄÆÆJ,›ŒŒL…{"333±qãF‘C¬ €™3gbÞŒyèÙ³§ØK;zyy!66·nÝLž<ÞÞÞøõ×_ŸŸŸˆˆˆ@xx8”””àééYn!!!˜6mvíÚÅÝC# W®\Axx8Ú¶m‹yóæaŊذa7ÒÂ萊F­m۶رc&OžŒëׯCNN®FË>ëׯÇüùóÁÆ 0|øpn~ll¬ÈÝ-,,*ý} —Èš\SsáÂ899¡k×®prrÂØ±c¥æ2±#FŒÀœ9s–– DEEaèСˆŠŠÂšQ£œœŒ»wïŠ|o€B}W"äóýùçŸÌÆÆ†åääðEâøÞ[L$¯ÏôΝ;ÌÚښ¥ŠŠòÒ~}300`:ub£Gf§OŸfïÞœãæUŽš"š`ïhrr2›0a›0a‚Èü²mmm–˜˜ÈÍÏÉÉaÌÜܜÉËË3###¶páB‘œyFFF,66–{~ïÞ=Ö±cGî¹±±1»wï^…Y7oÞÌtttØßÿ-2ÏÄĄ=|ø{žššÊ Ån[MÑ€ô·[öó]žp![²d‰Øy•}’’’˜¡¡!÷ý644dÉÉÉÜ|YYYVXXÈ=/,,d͚5»®;wî0===SeÞÚ̯Hc8vì÷Žhтéëë³þýû³³gϲŋó:ÀcóæÍc7ndŒ1öÍ7ß°ëׯs#AÞÞÞÌÕÕµÞó•¢€Fbߟ}ˆŠŠÂ±cÇjŒ'£!ÈÉÉᎥ$7ýºrå pìØ±F/ŒRwîÜ9±P’J÷økhh`ðàÁØžq£È|Æcxúô)ŠNŠ˜˜ŽoßЊMxzzÂÓÓB¡qqqðóóÃÏ?ÿÌýêÕ+î€J066Frr2÷<)) ÆÆÆæó÷÷‡ƒƒƒÈžXxþü9LLLDŠÕÅl‰ôóòòBŸ>}0|øp‘»»Weʔ)øé§Ÿ°xñbÏpppÀŸþ hÕªrrržcœ³³³Ñºuërë¹rå &Mš„ÇãË/¿”ÀÕL`` ëœ]IQRRâ~.,,Ä˗/ñòåKüç?ÿªª*f͚Åc:`êÔ©˜6mfϞììlôéÓ™™™(((Àþýûqüøq^ó‰C@âë닔”ìܹ³Ñž 4fÌ„……ñƒH]œ¶wîÜ9œ8qǎƒŒŒ|œ¶Í§ºêüÕ»òˆŒŒ Œqøðaôî݃ *×’••…™™```ÀM×ÑÑABBºví ˆ9dK__ñññ"‡9•uõêU|÷ÝwPQQÁ¢E‹žéžtéWŒˆËLš†-ZàÀ°µµ­ÑÉçQQQ8sæ ÷]vww‡¶¶67ßÌÌ ·nÝâî) 333‘u?~óçÏÇÙ³gñõ×_K`kjN ð~Õ­ÏqüøqìÛ·ÀÇb@MM _~ù%,X€‹/òÀ—_~ ¡Pˆ}ûö¡wïÞ€Þœ{cóæÍPPPÊßšhŠ‹‹1gÎ|õÕWpssã;Nrrrâ;iÀöìكÿý‡¢=œ<100@¿~ýpäÈ̚5 –––˜;w.¬¬¬ §§‡äädøúúŠì…µ··‡@ àîË D.i|øÀlmmYhh(ßQ‘Z%%%lɒ%ì×_å;ŠT’Ô9Uœ¶Ôï¿ÿÎzöìÉc,**Š?žéêê2yyyÖ¡C6gΖ‘‘Áœ>//¹žž0MMMŠ©©É\\\X^^7¿°°-]º”ééé1mmm¶mÛ6±í§€€0SSSæééÉûøœ b&&&LQQ‘YXXˆ\™cÆ ¬mÛ¶t jˆç” …lèСåŸãâŒ1öøñc6lØ0Ь¬Ì”••™µµµÈńB![ŽhkÛ¶-kÛ¶-ssscB¡°Êu—=G§¢¶«;¿*á€¢¢"VPP vßW*•––ÆäääXRRcìã•åääXZZZœg+«¢¿ÿ2ŒÑ%€U鍋ÜÜÜôÐ!uéÝ»w˜9s&¬­­1yòdŸãH%{{{>|˜FE$ÈÇÇC† AϞ=ùŽRïìììx9Š™¯vºÀÀ@XXX4Ú~Ē%Kàèè(rùŸŠþþÓ!@RêÕ«W˜6müüüªŒk)!MÕãǏáââoooôêՋï8„4zEEEò„45TH¡`þüùصkôõõùŽCˆT ÅΝ;qðàAhhhð‡& B*€õF9^ IDATÌþó¬^œ‡®Ó«zÒP1ưnÝ:<{ö §N¢Î!õš°°-ZŽà;!ä3Ñ¡R$887nÄɓ'©óOˆoÞŒÁ„  ¯¯mÛ¶Q矐zF!H‰ýû÷#22‡Bóæô±ò©¿þú ëÖ­ÃúõëéŒBxB!õ4¥€¯¯/²³³õ Ÿ©­ÂÂBxxx €€.\@˖-ùŽ$u„B!ÔÕÕ¡ªªŠ-Z@^^mÚŽ––:tè€7oÞð‘4TÒ8PÀ#Æ/^ mmm¬]»–ï8„HÛ·oÃÃî®®2dßq€–¬¬,ôôôp÷î]‘é¥7ÏéÞœ;OÉHcC'Ò8PÀ“ÂÂB8::bøðá°³³ã;!R¥ššþþþž{÷.:UUUŸ#I%¡Pˆ{÷îáʕ+åöÊjjjbȐ!ØŸ};ŠOŸŽÛ·oÓ}$()) Mñ6:ïß¿‡’’/mgggãöíÛŒŽÝ%%%Á‚ïuêþýûÈÊÊâ;†TÊÌÌ;nƃ÷ïßÃÁÁ³fÍ¢œš„|âÆðôô„@ À°aÃøŽ#u†+W® 33_~ù% „‚‚ØÛÛ£  :tÀþýûÑ»wo@XXuœ$,-- >„‘‘Fþýû7‰=ãáááxüø1œœœêœííÛ·#''§ÞÛmèddd0cƌF»#åÖ­[ç;†ÔRTT„‹‹K¹éTÔ³ÔÔTL›6 ^^^èÑ£ßq‘éééXŸ|9±jÕ*ŽiӆïHR¡ŽÃóæMdddÀÔÔ}ûöEÿþýEÞ£ÜÜ\èêê⧟~ÂÆé8ízòâÅ \ºt ááá(..ÆšQ£0zôh(++ó­Nœ={ùùù°··ç; !ä3PPàììŒÍ›7£S§N|Ç!D*0ÆpðàA;v ¿þú+Ÿúê+Ÿ#ñªl‡?33&&&b;üâäää4ڎgCðáć‡ãìÙ³xýú5† ‚±cÇBOOïh³oß>hhh`øðá|G!„|*êÉíÛ·±bÅ ìÝ»—îZJÈýóÏ?XŸ|9 —&yŒzÙVVºt邟}ûâÛo¿¥Î|V\\Œ›7oâäɓxøð!úô郑#G¢W¯^|Gû,°°°@ߟ}ùŽBù tp=ÇŠM›pôèQ:¬|ÜSœjÕ*äää`ïÞœPWWç;RœIHH@dd$nÞŒ‰ÔÔTèëë£_¿~ð÷÷GÛ¶mùŽG$€yóæèׯúõ눍Åɓ'áãã]]]Œ1¢Až7@£L„4TÔ±3gÎàôéÓ8zô(äååùŽCƒ±sçN,]º}úôá;N+Ûáýú5ôôôЯ_?x{{S‡¿ 133ƒ™™ 11.\ÀÿýßÿAII Æ ðaÃкukžSV-''‡Ÿ·„4tPÚŽiž>} ÿ&yh!e=xð˖-ÃÀáììŒf͚ñ©NŒzõ QQQž|ù2ž?=== 4ƒ ‚ŠŠ ßñˆ”ÉÈÈ@hh(þúë/aÀ€5jŽŽŽøŽ&֌3°qãÆQ¬B*F@`ŒÁÓÓ°råJžÓ¯7oÞÀÓÓðôô”ÚŽMm•vøÃÃÃñòåKèêêbРA8p`£œì©yyy ÃŋñòåKXXX`„ 011á;g„ 8räíÔ"€£@ÂJJJ0{ölXYYÁÁÁï8„ðа°Û¶mÃÕ«WáááÑh.{›’’‚ÈÈH„……áéÓ§èØ±#uøÿŸœ;«)ÿÿþºí‹-E*¢‘e“}$K–ЊPQ¶"e‰Œ("dËŸLŠÈ #d7Ôג¬-Œ"‘B‹ÖÏïï·ûs§¢toçVïçãá1Ý{Îù|^·¹÷vÞçœÏç¡+--ō7…øøxèêêÂÒÒýû÷Çã,—¥¥%ÂÃÃ9ëŸ"TQaa!1~üx˜››s‡Μ>}ppp€¥¥%×qjäëþׯ_C]]ÆÆÆ022‚²²2×ñH‘œœŒÓ§OãòåËhÒ€ ÌÍÍ1|øpÈÊÊÖj+++„……ÕjŸ„á£@Hrssagg‡Ù³gcذa\Ç!„wî܁¯¯/úöí 77·Zß9†ôôt\œzµÜÿ!C ¢¢Âu>>ujGùíÛ·žrå ®]»†7oÞ yóæèׯ† ®ãòMyyyžpá~ÿýwddd`ذa033C›6m„ÞWff&–-[†]»v œmBHí¢ †ÒÓÓ1mÚ4¬_¿]»vå:!µêÓ§OX»v-^œz…•+WBGG‡ëHßUÙ¿‘‘Qœºc+ixJJJpóæM„‡‡ãéÓ§044ÄØ±cѳgO¡Œžÿ>N:…å˗ !-!„KTÔÀ‹/0kÖ,։B„å˗/ DLL ÜÝÝ1pà@®#U*##ÿý7®_¿ŽäädÈËË£ÿþ011ÉQRBÄEÙÍÇ>|eeeŒ3#GŽüᛏ?~EEE°±±rRBHm£à=~üóçÏǁ ®®ÎuBjEqq1Ž9‚°°0ØÛÛÂÓI*òîÝ;ܺu‹¿Ã¯  €~ýúaäȑÐÔÔä:!œxñâ"##qãÆ HIIaĈ˜0aBµæó_»v-†Ž^œz‰0)!€6Pðbccáå兠  š„4Œ1?~{÷…ÅæF^ïß¿Ç͛7Ëíð1mÛ¶å:!bçÇžpᢣ£ñîÝ; <VVVPSSûævØŒy3š6mZKI !¢B@5ÅÄÄ`˖-8|ø05jÄuBD.::þþþ2dˆXÌìóùógüý÷߈ŽŽFBBadd„áÇ£]»vœf#€®ùòå ®]»†?þøϟ?G¯^œ0qâDtêԩܺ8~ü8) !Â&Åu€ºäÔ©S8zô(BCC9ß "DÔnÞŒ ôéÓPPPà$Ge;ü³f͂––'™©/äää`ll ccc”––âîÝ»8vìbcc¡££KKKôë×Oì.õ#„Ô š¢àà`ÄÄÄ`×®]’¢º‰Ô_qqqðóóCǎ±hÑ¢Z?ݟ““ƒ[·n•Ûá766†¶¶v­f!€!+»ùXLL $$$GŽ¡`„ÔTTÁöíۑ””„M›6ÑQRoÅÅÅaÆ hÓŠ ÜÝÝѲeËZé÷ëþ€€$ÈÈÈ`ðàÁèß¿?ôõõk%!äÛ8€ØØXdff¢€€ÇÇøñãkí{‚"\t(û;Ö¯_ØŒy3ÇI`ÓŠMhÖ¬6oÞü݁€5•››‹›7o"::‰‰‰••ÅàÁƒakkK;ü„ˆ©€€$žºº¢cǎü›yxxàÝ»w0`ÌÌÌП}{®cBªˆÎT‚1wwwtèÐ3gÎä:!Bõë×CQQ^^^"Ûñ/Ûá¿víœ{÷ƀ„vƒ"BˆhYZZâØ±cüKÊ”Ý|,** Ož<žžƌƒþýûÓg›1F@JJJàìì ###Lœ8‘ë8„Õ×;þ+V¬ú},òòòpãÆ \»v ϟ?Ç£~Bê°ââbL™2¡¡¡ß]7!!QQQxðà7nŒÑ£G×èæc„Ñš°žzõ* žÈ#ž?Žììlôîݛë(èׯg³¯p-)) )))\Çš7233±{÷n(++ÃÊÊ JJJBkû͛7øóÏ?ñÏ?ÿ@NNƒ ‚¥¥e…S ñríÚ5|ùò…ëb«mÛ¶ þҖ[·náʕ+XŒxqµ¶{ùò%Ν;‡ëׯ£žž#FŒÀøñãÑ€I%%„TU¹ ŽŽ†††˜>}:W™Èÿœ:u 7nDǎ¹ŽÂ‰iÓŠ¡W¯^‘‘á:JœPPP€‚‚‘üñýøñ#òòò ŠŠ†·oߢ°°«W¯z?Døz÷î3fpC,eff";;~~~\GáÔºuë0dÈüüóÏ?ÜFÙÍÇΟ?ÌÌLþAaŸ$„TM…ƒ€uttèºw1ššÊuÎ9884Ø3 uÕ³gÏpøða®c*ÒÖÖŠïûJŒxñ;wîä:çâââ°hÑ¢µÑŒysXZZÂÒҒó± 6 )) ={ö„••:wî\n;˜™™UžŒòãh B!„T(77RRRBœÿME7 C\\޵µ1vìXüòË/(--Å®]»°}ûvüú믘7ožÐ2ÒÐQ@!„ ={&&&"k_BBœzõB¯^œ?Fdd$vìØuuu|üø999ðòòBTTÂÃÃÑŽiS$&&âŋ"ËU×ÉÊÊbàÀ\Ç bŒ B!„TèôéÓð÷÷¯µþ:uêğ<`ÜžqÈÉÉdggã… 000À±cÇCCC¡ž™šO‚ƒƒqåÊ®c1&öŸ‡™©ôG·#€"uñ}HŸBHM!77***µÞ7c ±±±ÏÉÈÈ -- ýúõƒöîÝ YYÙZÏV\žpëD̉}ð5.vhªÚ'íl5ôÿšÒ\žpƒæ€oÆ.\ ())AAAMš4€€$š5kÖàgf"€Š$Ÿ¿Ê÷ñx<˜˜˜TžST›þ|o]aítñx<èëë—k1†Î; äšjŸŽCÈœºö>üøñ#–.]Š:@AAmÚŽ¥¥%®_¿.”ök‚nÖEjâë÷ÇãÿkÕªìí푙™É_.ŒÏœ_E#$$ÖÖ֜ô-!! ÀÍÍ ööö°ŽŽÄˆ#`ll ÈËËs’‹úB(())aǎÂjNä%ð\dd$9JD„¡.œ'NœˆÌÌLœ9sYYYžzõ*Ə… r¡bŒ1†žž8äçç ÌæBŸñ”™™ N.ÿ!„ˆžÐ €ÀÀ@lÛ¶ ?®tÒÒRøøø@KK ͛7‡ƒƒrssüÿœ²#Eù÷Q¥Ã‡C__ŠŠŠèÛ·/>|XávqqqÐÐÐÀ–-[øÏ-^ŒžÜ)D???,Y²€Â>œœpðàAe€³³s²áªKïÃ+W®`Æ hߟ=deeÑ®];Lž<·nÝâoSPP777š©©¡U«VpssžKwqq1V¬X¶mÛBMM U껊¿B~„††¶mۆsçÎñŸ«Êç€Þ¯µ/88“'Oæ:!DD„V4kÖ »wŠŠŠ*\' 111žxñ"’’’PTT„+VøÿË*ʎUEDDΜ9ƒŒŒ ˜˜˜`öìÙå։ŠŠ‚‰‰ áêêÊÞÜÜžqã &&™™™033«°¯­[·âСC „……á·ß~(*ª›³f͂¡¡!:Äÿƒ^ué}øË/¿`æÌ™ž~ý:òóó+lÛ×× žsçbccñàÁ¬Y³†¿ÜÏÏW¯^Åŋq÷îÝ ‹Šúö·áÇcذaˆŒŒ¬ôsQ[þœ£^•Ïœ_k_tt4Œ¹ŽAö/%%%ÌÚÚúßOÓ×Í,Zވ-]ºŽÂezzzìɓ'üÇééé¬mÛ¶®[Ñsÿþù͛7üǹ¹¹L^^^`ùöíۙºº:»}ûv…mîÞœ›™šš2Æ1bÛ³gÏ7ûLOOgZZZÌÃÃiii±ôôôÊVU˗/ø}ÕG&&& “‘‘a­[·f?ÿü3;rä+((`S§Ne¹¹¹Un«®œ?~üȖ/_κwïÎää䘶¶6[ŽhËÎÎæ¯£­­Íøãã㙎Žÿ±®®.‹¯à·ñíŸäwPUOŸ>ežžž?Œ=Ÿ=z0LQQ‘ikk3cccvòäIV\\̬¬¬„Þ_eŸ—ŽŽ4fmm-ð7Š*ŸQŸ_¿%%%…-^ŒX$m‹³‹/²5kÖpã›lmmٗ/_ªœÝ¿ß›eÿTUU™{ÿþ}…Ë•••ÙèÑ£Ù³gτ’_TïÙ2¢ø\“ú…ǘà!“ÒÒRLž<G­rñõ¬(………èÛ·/¶lق,SPP(w„GBB%%%åÚ©šíÊ~®l]mmmLœ8Qàˆé×ë@KK 7nÄ… ‘’’YYÙoöãåå…U«VÁßß_àÕêd«ªùóçãõë×hÖ¬Yµ¶«Knߟ{÷î <§šš tìØ—/_†‚‚B•Úªkï–âñãÇØ°aÞ¿ÏŸ"//¬¬,ÈÉÉŸ|ù%%%~þ/ÿwŽÊúþ‘ßAU={ö sç΅––ÖmO„ïÒ¥Kxþü¹Àse3ªtêÔIèÏÿý(Ó²eK 6 ›7oF‹-ÊmWÙç@”ï×oyñâvîÜÙàfœ±ŽŽÄÞœ{ÅúoÝMZÙ÷wZZæÏŸiii„„„”[ž™™‰-[¶ ::šå@Mˆz6¹‰'âØ±c"kŸÔ}BŸTFFAAA°ŽŽž†455qöìYŽk×®Âm…}ýæå˗1dÈ())ÁÝÝœÜrYYY̛7vvvðññùîÉýû÷qàÀDDD`îܹ7ntuu…šùk5¢E‹DÚןžaBBB-[¶„ŠŠ f̘Û·oÿp»ué}ü÷µëëë# šššüçÕÕՑœœŒÎ;¡¡¡Á_ÞŠM$&&¢K—.Õê[Ô¿ƒ=zÀÃãFmá122âÿ,##ƒV­ZAMM Ó§OÇÙ³gEÞUwt*ûÔög¶!{ðàŽŽŽÄzç_ÊÆ§èëëWž\YYîîîü›¢`ɒ%8vìc°¶¶†ŸŸ?â[‡Ÿ·üÿ磎ŽkÖ¬ÁþýûññãGŒ?Û¶m£ÉIˆhüû”@M/*Àìíí–0cccöèÑ#–››ËnߟͿ‡1ÆZŽhÁ=zTiەýü­åiii¬cǎ§3+Úö{íäää°®]»²˜˜Æc§OŸf={ödyyyÕÎVU å ÖµkWæëëËÞŸ}Ë_V“K€ÊˆóûðçŸf‡fIII¬  €%''³Y³f1þ:˖-cÆÆÆìÕ«WìÕ«WÌÈÈHàò___öË/¿°€€$öæÍæääT¥ŸäwPUt øéÒ¥ kÙ²%ëÕ«ÛµkûôéYm^T‘ª|Dù~ý–†x û矞Žñ]ºèkoߟe***.ÏÌÌdžžž¬OŸ>Œ±ÿþ}þú»yȐ!lŊ•¶ýœŸclãÆlèС,99™}øðM™2…-X° Ú¯‘1ºˆ|ŸÈ €ÒÒR6bāe%%%lëÖ­LOO)((0É_ŸqãFÖ¬Y³*ílUçÃõæÍÖ©S'¶jÕªJ·ý^;vvvü1e6mÚÄŠM›VílUÕ €°””” — £ç÷áõëי••ÓÐÐ`rrrLKK‹Í™3‡effò·ÉÏÏg...LUU•©ªª2–ŸŸÏ_^XXÈ~ýõWÖºukŠŠŠÆvîÜY¥ŸäwPUTˆŸ›7o²ŒŒŒ —q]Tås Ê÷ë·4ŽàéÓ§lÖ¬Y\ÇšaOÁWc0555þß«ïÏú‘à{c]ªƒ ò=B)ˆh4„à[ª[ñ@@ÝB; •khÀ€I“ØË—/¹ŽQ%ÂܲeK6yòdB¹lÝÒÒRöüùs6`ÀÁcLNNNà`L~~>“““«°ŸÊúþ7yyùrE‡„„Dµ_#cô¹&ß'ô1„B©[þþûohjj ŒœhXƧðx<èêê"$$?ÿü3Œ¿;>KNNyyyüI,ÒÓÓ˵ùoßëBˆ0 í>„B©›|}}+€€ü—ŠŠ& €#GŽÀÆÆ®®®HMMEjj*\]]accÃ_·W¯^ð÷÷Gnn.RRR0kÖ,¶TTTÊݰÒÉÉ 3fÌÀãǏ‘——‡;wî`ÜžqµòÚHÃC!„Ҁýþûï2dš7oÎu±7cÆ ìÙ³žžžèÔ© ```}}},[¶Œ¿Þ®]»ð矢e˖=z4,,,Úñðð@¿~ýΞžžÀÔÔfffhÑ¢œáèèXk¯4,t !„Ò@}þüûöíÃï¿ÿÎu”Zñõ%?ß»ü§¢åÇÇðáÃ[·nÅÖ­[+ܶK—.žyóŠÀs¶¶¶üŸ,X€ ,—€‹‹ \\\Ÿý":@!„4Pžžžøõ×_!##ÃuBH-¢€Bi€þþûoHHH ÿþ\G!„Ô2ºˆBi` ±råJ;vŒë(„ÐB!€Y¹r%\\\Оqc®£B8Pî Çƒ€€$¬¬¬žÈ#RŒ1$&&¢}ûö\G©eee®cpŠÿþ˜:u*×18U\\Œ¬¬,ŽhÑ¢Òu^œz…-Z@NN®“}Û×Óáñ&##S/¿ï…ÅÒҒëBwéÒ%ÀÄĄë(ßÔ¡C0Æ ++ ÈÊÊ¢e˖PSSCJJ ×ñ©Óx¬*wÁšG&Mš„;v@II‰ë(„|Wii)ÌÍͱ~ýúJ ×/^ÀÍÍ 3fÌÀèÑ£k9!!€.ÉÈȀ££#Ž?YYY®ã|ÓðáÃqþüyç$$$вeKtéÒQQQbÿž2qâDºŒ‹|Sƒ»hÒ€I å:!U"!!]»vaþüù(**ªpvíÚáøñ㈍Å‚ PPPPË) !uAii)fϞ ±ÞqNOOGHHÔÕÕ!%õÿ*(++ÃÈȏ=‚šš‡ ©û\0räHœ={–ë„T™ªª*æÌ™ƒU«VUºŽ””ŒŒŒ0f̘šš">>ŸBꂵk×ÂÂÂ;vä:Š€ÜÜ\DGGÃÃÃVVVðöö†ŒŒ ÜÝÝѲeKðx<ŽiӁ8þ<Á'DÜ,@RRRÐ××Çœ{÷ðÓO?q‡*111ÁŸþ‰‹/ÂÈÈšÒõŒŒŒÐ­[7̙3ýúõƒ««k-Š$„ˆ«S§NáÝ»ww«åJqq1îß¿èèhÜ¿4hf͚---uååå1jÔ(£Y³füç(pc-"šU«V\G b®Á€äädlÙ²[¶lá: !UVPP333UipxPPþúë/lݺ͛7¯…„„qtïÞ=øøø 44Tà’šÚ”œœŒèèhÜŒyŸ>}BŸ>}`llŒž={‚ÇãUºÝ‡èû‹hL˜0G«™SùžGÁËË aaaßü£Y&>>K–,ÁÒ¥K1pàÀZHH'ééé˜:u*BCCŽ ‹ZÙÿ­[·ðþý{tîÜÆÆÆ4hÝu˜1Ð` €   ÈÈÈÀÚښë(„TËΝ;Áãñ0{öì*­ŸŸŸ%K– qãÆðöö†ŽŽŽˆBÄA~~>,--±eËèè舎¯wïÞ!&&ÑÑÑHJJ‚ŽŽŒ1|øp4mÚT€}Bª¯Áùùù°··GXX×Q©6;;;,ZŽݺu«ò6çÏŸÇŠM›àççW­í!uOqq1ìììàâ₟}û œýŒŒ<ÜžqÑÑÑHHH€¢¢"ŒŒŒ0zôhhhhœ?Bˆp5Øœœœàîîmmm®£R-YYY˜>Û¶m£ìR‡-[¶ ššš˜5kV•Öÿþ=.]º„èèhŒ~ýêêê4p—¢Á™™™˜7oBBBžŽBÈÉÈȀœœ="""ª5àk±±±ðôôÄÒ¥K1xð`!'$„ˆRii),X€:ÀGhà IDATÙÙ¹Òõ*ž;jÔ(ŽnݺBžÖà ˜>}:–/_޶mÛr…rùòe„††bçΝ?ÜF^^<<<КQ#xyyÑ)Bê€ÂÂB̜9#GŽ,7­õ×wïÝ»)))`À€èÕ«G‰ !†wïÞEDDV¯^ÍuB~˜ŸŸTUU1uêÔµS6]èêÕ«a`` œp„¡ËÊÊÂôéÓáì쌡C‡ž››› žK)‡ €ÿ3f Nœ8AG=IÅÃäɓ±xñbüôÓO5jëÓ§OXºt)7nŒU«Vѝ; 3<Àâŋ±xñbdffâÒ¥Kxýú5ôôôhà.!仚øŸß~û 222°±±á: !?,++ 666 C“&MjÜÞÙ³g±uëVøúú¢GBHH©‰ÌÌL¬[·QQQèØ±#TUUallŒaÆ¡Y³f\Ç#„ÔTüOAAlllÁuBjäþýûØŒy3:$”ö²³³áéé‰FaõêՐ––J»„ïËÏÏÇõë×ù×ñ'&&¢K—.غu+455¹ŽG©£è‚Àÿ‘••EûöíñŸÿü‡ë(„ÔH÷îÝѧOlߟ](í5kÖ Û·oǀ`ee…'Ož¥]BHy%%%ˆ‹‹ƒŸŸÌÌÌ`ee…žž8tèÐÒÒÒ8zô(Nž}ŠæÍ›cȐ!:t(ÔÔÔ*ÝîñãÇðõõE£F°jÕ*ŽlÙ²SB**ŒÒÒRØÙÙq…¡˜3g&L˜ccc‘ŽŸ‘‘_ý***tßLŸ>ÙÙÙt€XLáöíÛØ¹s'LMM¿»~BB6nÜyyy,[¶ êê교’ҐQP¢¢"Œ?QQQàñx\Ç!€ÆŠŠŠ`ii‰M›6A[[[dýDEEaçΝðööFïÞœEÖdkk‹ýû÷Sá%F|||`jjŠnݺUºN\\vïލ¢¢",[¶ ºººµ˜ҐÑᢠHKKcÀ€ž|ù2×Q iiiìÚµ ...ÈËËY?cƌÁ‘#GpèÐ!ž¹¹!77Wd}R}ùòAAA055ÅéÓ§±fÍ}°iÓ&‘÷5hÐ œ8qçϟ‡³³3>}ú$ò> 'ÙÙÙØ¶mƍ‡œ{÷bêÔ©øý÷ßaccC—kB8EÀ7,\ž\Ç D𜜜ðìÙ3œ;wNä}ÉËËcíÚµ°³³ƒ¹¹9Nž<)ò> ¡¡¡ÐÕÕEDD6nÜ}}}®#B*ŸI[[Ÿ?Æëׯ¹ŽBˆPmÛ¶ HII©•þ …»wïÂÑÑïß¿¯•~IÃVÕID5ÙÃìÙ³abbBwÌ&„ˆ*Ÿcá…زe ×1*iiibîܹ"ü5YYY¬\¹nnnpppÀþýûA“qãë^ÇÿתU+ØÛÛ#33³Âå***3f ž?.ôÕÙF__¿Ü{‡1†Î; ŽYÕ÷œ ! ßñÓO?!11ÙÙÙ\G!Dš444°råJ̜9³Vw€ºvíŠÈÈHHKKc„ xøða­õM*Æc qqqÈÏÏÇŒyó*\þôéSôìÙööö%ý/DEE < EEEŽBHÝB@8;;#00ë„FŒÕ«W×j¿<vvv8pàvîÜ 777äääÔjRž††¶mÛVéøeeež»»ãÞœ{€‚‚ž¹¹AMM ­Zµ‚›› øëWt„¿ì¹¯ÿûõz¥¥¥ðññ––š7o‡rÓÉ.^Œ~~~Ïùùùaɒ%öåä䄃 ,;pàœËåäñx8|ø0ôõõ¡ššˆŸ}ûR‘J©wšš‚¡C‡âï¿ÿF~~>×Q:[[[äääàØ±cµÞwóæÍùS†š››ãĉµžú÷ù×>|ø€õë×£k×®___$$$àΝ;ˆÅƒ°f͚*õSvÖ©ììB™€€ÄÄÄàâŋHJJBQQV¬X!°­¹¹9222pãÆ @LL 233affVa_[·nÅ¡C‡ Ão¿ýVéå8sæ 222`bb‚Ù³gWé5BHÁH•?~œrƒ‘())aVVVìΝ;œe(,,dÌÚښœxñ‚³uє)SXAAA•×ÿú«ÿëŸÓÒҘµµ5³¶¶Xþõ?555–’’ÂcL[[›%$$ð׍g:::¶ýœŸËèéé±'Ožð§§§³¶mۖÛf÷îÝÌÔԔ1ÆØˆ#؞={ŸÙ~zz:ÓÒÒbLKK‹¥§§Wšé͛7üǹ¹¹L^^Ÿ\ÎïYœz5»ÿ~µ·#„Ú@gªh„ 8sæ Š‹‹¹ŽBˆÐIHH`ߟ}X¶lg³^IKKÃÕÕŸŸŸXžp!ŒœœQXXÈI–†Šìš= ))‰­[· ,gŒ¡ŽŽϟ?‡ŽŽîÞœ xýú5޵µùëéêê"--­FY^Ÿ| ===É¯^œ*·žœœ=îܹƒ£GâÁƒ°³³ûf»ªªª°µµÅºuë0gΚªªVºn«V­ø?+((ÐÙ_BHœC@IHH`„ œ\&AHmhÜž1víÚ'''ëžk›¶¶6Ž?Ž^œzÁÜÜ7oÞä,KCÁþwÎÛ·oŒ-Z”[‡ÇãAWW!!!pvvÆçϟ¡®®Žäädþ:‰‰‰ÐÐÐà?–““˜e*==œ\›ÿŠ©©‰””~&ÆJJJÊ­'++‹yóæÁÎή®®••ýækŒÿ>8€ˆˆlÚŽ ‰‰‰ß\ŸBê3*ªÁÎÎAAAþ1"€>ÐÒ҂««+æÎËuŒ;¡¡¡8uꊊ$ÜÑÔÔĀpäÈØØØÀÕÕ©©©HMM…««+llløëöêÕ þþþÈÍÍEJJ f͚%ЖŠŠ ?~,𜓓f̘Ç#//wîÜÁžqã*Ìâáၢ¢¢rƒÿ-77¶¶¶Ƅ °{÷nLœ8‘ŽìB,*ªAZZt€ÔkFFF044¬ò`NQRTTÄÚµkáìì {{{ìß¿¥¥¥\Çjðf̘={öÀÓӝ:u‚  ¯¯e˖ñ×ÛµkþüóOŽlÙ£G†………@;èׯŸÀ™˜ššÂÌÌ -ZŽ€³³3k”×ÙÙ...}ŠE‹q…ZQRR[[[žžž oߟ\Ç©’ÌÌLlÜžÿüó|}}Ѷm[®# ««+ÒÒÒ !Aó:p-11***ÈÎÎÆ‘#G ««Ë_VZZŠ . ((’’’˜3gz÷î]í>Š‹‹ñÏ?ÿð/%JHH@VVÿŒAÙ¥DÝ»wGãÆ…ùò!õB2aÂ8pJJJ\G!€VäååÁÚÚþþþèС×qªìéÓ§X»v-Zµj…¥K—¢iÓŠ\G"õÈ£G†›7o¢G˜6m:vìXnœôôtlÛ¶ =‚££#F Wãþ_¿~Í/ =z„ׯ_£€€mÚŽáŸ1èÚµ+TUUkÜ!€î¢@H®]»†³gÏò~ÒŒÿvvv8xð`Û¡žrå 6l؀aÆaÖ¬Y••å:©gŽØØXÀÎÎÚÚÚëäää`ÿþý8wîŠM›sss‘œÉÉÊÊ8cðæÍ”––BYYYàr¢ç«M999(**â¬ÿ†¬qãÆ’’â:©ET‘¥¥% ¡¡ÁuBjMrr2æÍ›‡ÐÐP4jԈë8Ս-[¶`ìØ±ppp ?‚DèJKKqùòe;v ïÞœÃøñãann.0n¬ššDDDaaa!”3ßóuaœœŒ×¯_ã˗/——GçΝù…A»víD~‰Y÷îÝahh(Ò>Hy<@@@~þùg®£ZD€ÅÇÇcϞ=ضm×Q©U±±±X¿~=Ž9R'w KKKqâÄ üöÛo°··¯µ/ÒðàÔ©S8yò$dee1iÒ$ :”ÿ~ûòå öî݋¿þú èß¿?'9E5ei^^Þ¿_á=&Nœˆcǎ óe*Øžq# @@C€988à×_øEHCpöìY„‡‡cïÞœuv0jAAvïލ¿þú nnn066æ:©ÇÒÒÒŒ«W¯ÂÀÀÓŠMãNÏÍÍņ ðèÑ#¬]»:::§ý¯šNYzìØ1̞=cÇŽÅæÍ›¡¬¬Ì_F7šh˜š²””x{{ã·ß~ã: !µî÷ßÇ¥K—°uëV®£ÔHNNWWWüòË/\G"õc W¯^EPPòòò`ooaÆABB/_ŸÄòåË¡££±«RÕ)K÷ïߏ;w‚ÇãAMM cǎ…¿¿?5jDGšh˜šE‹aüøñ•Þô…úì·ß~Crr2V®\Éu”ËÉÉÁŽ;pýúužººbèС\G"õܧOŸŠ3gΠ_¿~˜>}:”••qéÒ%øûûcþüùuêÌTQQ¿0xòä Ž9‚Ǐó—KIIA]]óæÍÃßÿ°°0Ó6LT4LTˆÀ§OŸ`ccƒÓ§O×ÙK!©‰uëÖAJJªÞÜãóçÏ ĵk×0þ|q‰4×®]ÃîÝ»QRR‚¹sç⧟~ªU«ðùóg¬_¿ŸNÞü«K—.HHHà?nÜž17nŒüü|èèèàΝ;Šk˜šh˜hïTš4i‚±cÇ"$$„ë(„pÂÃÃï߿ǟ}ûžŽ"7ƒ%Kpøða\¹ržrå ×±H=7`À>|ë֭Ù3g`ccƒ=z`ʔ)033õkמŽXmŸ?F·nÝ0jÔ(¬Y³§NB||<>|øÀ逄44t@DJJJ0vìX„‡‡×É£4„Ôc sæÌÁ!C`iiÉu¡ÊÎÎÆ¶mÛ‡3f`ÔšQ4k¹‚‚ãĉ6l=z„6mÚÀÓÓ³^œm®î€¯?sÊÊÊ044ÄæÍ›ÑŸ}{¡eâñx(ÛMúº?UUUŒ1›6mâdež¯sh˜êþ7†˜’””„»»;üüüžŽB'x<¶oߎӧOãìÙ³\Ǫf͚aùòå Abb"ƌƒ   sÔc²²²pttDTTtuuñîÝ;<~üøðá×ñ8Ác OŸ>EϞ=aoo_+ýÅÅÅ!??óæÍã4!?Š 2dñòåK®£ ìÛ·ûöíí[·žŽ#tŠŠŠpuuÅï¿ÿŽÂÂB˜ššbߟ}(,,ä:©Ç$$$0vìXœ`ýúõèÚµ+ÿ9KKK̙3©©©xùò%444°téRÀˆ#pùòe@jj*\\\øÓ–^¹r#FŒønŸÿÞ!ÿV___$$$àΝ;ˆÅƒ°f͚*œ¶²KÊÎ.” @LL .^Œˆ€€$aŊUj“0"r‹/fW®\á:!œÊÍÍeŠŠŠ,66–ë(µ"66–MŸ>ÙØØ°[·nq‡4ÅÅÅlòäÉÌÀÀ€Íš5‹œyó†ëHÕbeeU­õüSSSc)))•®ÿñãGÖºukÆc‘‘‘lâĉŒ1ÆÖ­[ÇZŽhÁöîÝËÏqêÔ)~_÷W&--Y[[3kkë*åÑÖÖf üuãã㙎ŽN…mWô\EËõôôؓ'OøÓÓÓYÛ¶m+}ý•ñ÷÷§ïšˆÎÔOOO¬[·¥¥¥\G!„3 8|ø0ŒŒŒððáC®ãˆ\¯^œ°wï^lÜžþù'ƍ‡ððp'@DFRRÁÁÁ°°°@AA–.] deeqMdc(--ÅóçÏ¡££ƒ»wïò—ÅÆÆbèСhÞŒ9x<š6mŠ7oތŒŒpóæM@HH<ˆƒnݺ…!C†TØ_ÙQÿ=z@RR²ÜM+ËóúõkYŽtuuk|Šæå˗ÐÓÓãgjÕª^œzU£6IÃA@-hÜž1ÌÌÌø_.„4TMš4AHH<<<®¿­ÏÔÔÔàíí#GŽàÝ»w7nV¯^Ý /Ó µcɒ%022‚œœLMMáàà|üø‘ëh"Áãñ ««‹8;;ó/å±¶¶†œœ=Q\\Œ> €€КQ#hkk#,, òòò=z4Š‹‹qòäIèèè Q£FöÅþwÎÛ·oŒ-ZT)ºº:’““ùë$&&BCCƒÿXNNyyyüÇéééåÚü7MMM€€€ð31Æø¯ï¡ –L›6 'OžÄ»wB§š6mŠàà`ž»» ÜŽŸSTT„³³3Μ9ƒQ£FÁÇÇãǏ§³D$lmm1jÔ(ìÙ³ááá3f ŠM›???äççsO$4551`À9r››‹&Mš@QQ/_ŸÄŒ3Ö9r$,X€)SŠ&Ož Œ9RèylllàêêŠÔÔT€ŠŠÂÕÕ666üu{õêäææ"%%³fÍhKEE¥Ü÷¥““f̘Ç#//wîÜÁžqㄒ4^~ÔàÄÇdz™3grƒ±ðîÝ;6räH–˜˜ÈuÎdee±íÛ·³Ñ£G³+V°—/_r‰Ô3QQQÌÖ֖3Æ;uê311a‡b%%%§ô#cþíܹs¬gϞŒ1ÆÎœ9Ã:vìÈ€¥¥™ŠŠ&Ûºu«À6wïÞeRRR,##ƒ1ÆØÛ·o™””»wï^…}|o—é[yòó󙋋 SUUeªªªÌÅŅåççó׋g†††LAAuêԉ Ž·qãFÖ¬Y3çJJJØÖ­[™žžSPP`,22ò›+Bc&ºX-[Œx1FŽ ###®£¹wïÞÁÎ΁ÐÒÒâ:§nߟƒâíÛ·055…™™š4iÂu,Rüõ×_ÇîÝ»!!!ÒÒR#,, ‹/Æ AƒžŽ ú7#ÂA7k˜è Zæííõë× ÌÿKHCÕ¢E 8pÎÎÎøçŸžŽÃ©>}ú`çΝ8zô(”••1wî\˜››#<<EEE\Ç#uØðáÃabb‚ùóçøï}ìììpìØ1\ŒxVVV×§Bê?*j™‚‚\]]±qãF®£"ÔÔÔpðàA899!11‘ë8œ“••Åرc„={ö ##–––˜?>nߟ :iK~„™™ºté‚Õ«WóŸSTT„··76n܈5kÖÀÍÍ­Þ&„¢€&&&ˆ§#.„üO«V­pðàA̙3§ÁÌTÊÊʘ3gNž< gggüõ×_7n–,YRîŠjiiiPUUÅŋ9JKÄ݌3ŸŸÃ‡ <ߊMìÛ·“&M‚­­-¶lÙB³ÉRÏQÀ‘õë×cñâÅ\Ç DlŽlÙ¡¡¡ððð@||<×qÄNûöíáéé‰S§NaöìوŽŽ†‰‰ \]]qíÚ5#33“&M‚««+Ýw„TÈ××çϟǵk×Ê-ëÓ§Nž< %%%˜ššâÒ¥K$$„ÔÌ!ŽmÛ–––\G!Dl|üø“&MÂʕ+a``Àu±÷ðáC„……!"" yyytíÚ‘‘‘øçŸàíí]éŒæäÇñxŒ:9h5??Ø·oÔÔÔ*\'77ëÖ­Cjj*Ö¬YSézÂDƒ€¹Aƒ€&*8T\\ SSS„††Òl„|%77vvvXŒx1ýQª‚ììltëÖ­Ü]@[·n {{{téÒÖÖÖ¥«¿êòë˗/1wî\DDD@ZZºÒõáááÞœ{cᅐ’’YŠºüû¬Ëšh˜è IIIÁÛÛK–,á: !bEQQ‡Ɔ pãÆ ®ãˆœóçÏ#''­[·FëÖ­Ñ®];hkkCFFÈÈÈà:"3m۶ŬY³°bŊo®§««‹ãǏ£sçÎ033£Ï#!õëÓ§š7oŽ?ÿü“ë(„ˆ„„„`Û¶mžpá×q̙™’’’œœŒW¯^!%%IIIHJJŸ}ûвeK®#14fÌäççWéZÿ±cÇâèÑ£ˆŽŽÆôéÓé®ö„Ôq¢;—GªÌËË ŠŠŠ044„’’×q²²²8tèŠNŠ‚‚Œ5ŠëHbIRR’Ÿ;ÈY·n&L˜€Ÿ~úé»ï!EEE¬X±<€ƒƒŒáââ áK|ÿþ=¬¬¬„Ò֗/_ðùógdggãÇPSSƒŠŠŠPÚ®oÞŸ}Kß­ wïÞÅ®]»°{÷n®£"vJJJ0wî\ôîÝ\Ç©S""\ «ë IDAT"PXXHcD Ÿ\³‹ 00°ÊÛ0ÆpàÀDFFbõêÕèÞœ»V-τ ’’‚œœ~ðùóghhh ..ªªªœf$DœÐ%@b¢GPUUÅɓ'¹ŽBˆØ‘””D`` ^œz… 6p‡ˆ!Çu„:ËÀÀòòòž~ýz•·áñxpttÄÁƒ±cÇxzzâ˗/"Lùý|ˆQ£F¡qãÆhÔšFމ¬óñãG,]º:t€‚‚ÚŽiKKËju:{4jÔÓŠMî]»~hûAƒáĉøë¯¿àììŒÏŸ? 9aåþùç899!,,Œ)’šš‚ƒƒÑ£GZËAH]B€˜éÞœ;455ÁuBÄ֔)S0~üxL™2ùùù\Ç©÷c`Œ!..ùùù˜7oYbb"ŒŒŒ`llŒçϟ#11#FŒÀСCñìÙ3þz'NDff&Μ9ƒ¬¬,\œzãǏÇ… ¹xI€–––8þüŸ…–——ÇÚµkagg333‘_ÒúîÝ;,]º‹-‚““öî݋}ûö¡I“&Øœ{7~ùå‘öOHÆˆØ)**b&&&ìíÛ·\G!D¬Ýºu‹™™™±ììl®£ˆ­'N°£GVk›¯ÿ4üûÏDzz:SVVæ?ž77—°ñãdz .-! cóæÍÃŋË Š#„jÛ¶-°fÍœ>}šë8õNÙQÿ=z@RR[·nå/SQQAZZZ¹m^¿~-0#K“&M°jÕ*Ü»w¹¹¹8uêÞœ{‡É“' l£­­Í¬««+Ðvjj*tuu+͹iÓ&ØÛÛ£wïÞÏ¿|ùzzzƒ™_œzUœ_B2xð`ÜŸ}999BiORR ,€fΜ‰ààà*mWXXˆ={öÀÌÌ êêêøý÷ßadd$”L„4tTˆ1‡ÀÀ@,]ºyyy\Ç!D¬5nÜ!!!ˆŽŽØA%5ÇþwyÌÛ·oŒ-Zð— :‡.·MPPP¥;kÐ××G@@®\¹Â^]]ÉÉÉülj‰‰ÐÐÐà?nÓŠ +Íyùòe„‡‡—»W„ŠŠ&RRRø¯ƒ1†’’þrššŒéÓ§ãÀBmS[[HKKƒ­­m¥—!((æææPRRŸþ KKK¡f!€¡£@Ì)))ÁÃÊ%€ $%%±eË0ÆàääDӄÖ///ìØ±›7oFzz:ÒÓÓ±yóflߟ˖-ã¯ghhˆàà`$''£°°)))ðððÀ€øëØØØÀÕÕ©©©HMM…««+llløË§N $''#==ÎÎÎYZ·nË—/cÿþýX»v-ÿy'''̘1?F^^îܹƒqãÆñ—«ššàñãÇ¢øõÔYcǎŹsçª}_€ï‘””Ä’%Kàáá;;;DFFò—aÿþý7nddd KKK*Ð*ꀁ¢E‹8zô(×Q©\]]ajj |úô‰ë8õZ‡páœ?íÛ·‡ššŒŒŒpñâEtìØ‘¿ÞŠM›púôi 4M›6ÅСC!%%%p9ˆ§§':uê@___ ˆpwwG¿~ý0xð`ôìÙݺu+—G]]1118|ø0V¯^ pqq©©)ÌÌÌТE 8;;ÃÑё¿‡‡úõëG;š_áñx?~<¢¢¢DÒŸŸŸ>"##ñäÉØØØ`çΝ°°°€ŽŽ4N:kkkHHÐ. !¢ÂcŒŠ=š JKKaii hiiq‡:áþýûøõ×_±sçNhjjr‡(,,„µµu­ôWXXˆž={bùòå˜8qb­ôɕ‰'âØ±c\Ç™OŸ>aæÌ™ Iû……… ÅÁƒññãGøùùaذa"é‹"ˆÊë:BBB[·nŜ9sPXXÈuBê„îÝ»c×®]˜={6þóŸÿp§A‘‘ÁÁƒáææ†ŒŒ ®ãhÒ€ äååñæÍ¡¶[6ž·ì2¬èèhÜžqýõæÏŸ/_Ÿµ?BHyTÔ!pqq··7×Q©3ÚŽiƒcǎaýúõ á:NƒÐ»woŒyóF` R7ÙÙÙU8ÈûGðwüåääpæÌØÙÙARRrrrذaLMM1nÜ8Üœ{W(}B*F@cbb‚‚‚œ;wŽë(„ԍ7ÆÑ£G‘šš '''¡l$€Ÿúå—_S£›€åææb˖-?~<”””ðÇÀÎήÂkü‡ ‚°°0lߟ~~~(--­I|BH%ššƒÖ®]‹íÛ·ãŋ\G!€ÎàñxX²d LMMannŽôôt®#"öx<úô郞žžjo[¶ãoii uuuüñÇUšÕ§iÓŠØ¿?޵µaeeUá}&!5#ÅuR}222Ø»w/®#UèãǏ5Ÿäå塚šÈwҁ P]ŽŽŽˆ‰‰ip™'Ož ,, ÉÉɘ?ŸÔFô©®®Æ7ß|ƒ{÷îa˖-PUU•Êv éÊè @fmm+W® <<ü¥ qÚ±XŒU«V¡gϞ|G!ȍ7=А%%%ìÞœ»wïÆÇŒmÛ¶AII©ÕÛ%€#255ŝ;w$ævš•žžŽÐÐPäææÂ××ÁÁÁRm[NNK—.Å¥K—àêêŠ/¿ü²ÉW#!õ£ ƒóóóÃG}„€€$Œ÷Þ{|Ç!€Ã™3gLLLàêêŠ;v OŸ>|G’*ÄÅÅááÇ|GétºÒ0§NNNˆ‰‰ÁÒ¥K¹×þþûo„……1†%K–À¢U3ŒûŒŒÄ‚ зo_¬ZµŠ& #€…šèÂÃÃáââ}}}˜˜˜ð‡gÜžq033ãfˆuttä;’ÔØÙÙuÙIÐZÛÔ©SùŽÐfÞ}÷]lÜžðÛo¿!<<:::Xœz5z÷îÝf9ÔÔÔ°ÿ~|8>üðC,ZŽ&Là- !];ë$ôôô°~ýz|üñÇš©©á;‘‚–ŒšA^œœV¯^ ooo8::"99™ïH„𪢢Ääɓ¡¯¯OOO¬^œš×ƒÿZýû÷Gll,~úé',\žæ÷ €šèDÞzë-ž»»cŊ|G!\S‹ÎZ€ØØØàرcØŒy3BCCùŽCH›{úô)BCCáâ∏Ç7ß|ƒ_ý•çd’‚1cÆÀÁÁiii|G"€C  “quu…ššvíÚÅw”f“‘‘áÚÚژ}úÀÕÕ.\jæ–šïÀ\FF–––/ÍèɃ………Ä:Mž3"Ü£G|ûí·ÐÔÔČ3PPPÀw$BZÝ£G°zõjLŸ>ÆÆÆHHH€——ºuëmmm<~üž]þ»wppÀΝ;±páBÄÇÇó‡v €NhŊHIIAll,ßQš1Æîܹƒ!C†ÀÛÛ»MÚ»víÊÊʰ`Á‚&çqwwG~~>QXXˆ€€$8::bɒ%­šùu(++#!!AâµØØXšššð”šýóòòÂòåËáååÕîÎ~"-—/_†··7V¬X$$$`ʔ)/-7pà@ÜŒy“‡„ …øñÇñçŸbÑ¢EÔ%ˆ†0Ò)UWW3véÒ%Þ2̝;—åçç7yù¿ŽOŸ>eJJJÜó;wî0Ö³gOÖ£GæääĞÌY@@€TºÄlÛ¶ýòË/¯® ýøãÌÁÁÍœ9“)))1KKKVYYÉw,BxC@‘œœÌ&MšÄõo ¯{ÀÇ™žž{úô)cìy?ÿ°üü|V]]Í $Ö±±±aGeïŒócŒ±wÞy‡EGG³1cÆÔÛFc§å©Oaa!SSSãž7vÀÌ̬Á+™™™ÌÔԔ}ýõ×ïõïß¿Á_ä222 îÏW_}ÅäääØúõëë}¿îßÿúë/Ö»woÅôõõÙœ{÷]§¡×ӑ ƞßÈæÍ›×àwƒ¶”••ÅþóŸÿ0;;;¶k×.VZZ*µmß¹s‡ùûûKm{m%,,Œiii1LAAð‰ÞPЅ\Ÿ|™999±ŠŠŠ6iïu Æ›:u*Û¹s'cŒ1===ÍÊËËÙýû÷™‹‹‹Ä:ëׯglëÖ­Œ1ÆBCCYïޜن êm£¹À‹y†Οûî;vÿþ}VQQÁùäfggÇ-¿bÅ fkkË233Yff&;v,[¹r%÷þÚµk™ »ÿ>ËÍÍån®­Û~vv6ëß¿?[·n÷ޖ-[˜­­-»uë+--eüñ×­‡1ÆzõêÅnݺÕèþŒêýÚ¿?{öŒœùæ›\wøøx6dÈîò?ÿsýúufooÏbccùŽBºšššvòäI6sæLæããÃþüóÏVkËÉɩնÝîÝ»Ç ^êÖyçΟ£ *º˜'N0oooV]]ÝêmI£øùçŸÙ!CcŒ%&&²þýû3yyy& ÙÖ­[%Ö¹~ý:“““ãºñäåå1999ö×_ÕÛFK €ºy.\žÀÜÜܘSTTdFFFlþüùû\VVÆ|}}™®®.ÓÕÕeŸŸŸ]~*++ÙòåËYïÞœ™@ `;vìš·ýÜÜ\6`ÀîÀžŠŠ†mݺ•™››3eeeööÛoKx~óÍ7LCC£Åû[ûw///îZ›7of>>>¯\çUÛlªŽZ0öŒ»Åʕ+™ËÎÎæ;é"222Xpp0³··g[·nmtt2i˜3gËÉÉiõv€åúõëlôèÑL(2EEE®2d‹Å|Ç#€ÍÉ0Ögô ­*::QQQøöÛoÑ­[·VkgÞŒyX»v-zöìÙjmÎçÆˆ‰‰APPßQZìÞœ{X¹r% €ÀÀ@(**ò‰t2ˆ‹‹CLL 0}útŒ7®Ífç>vì***0cƌ6iOZD"¢££±wï^€§§#//«W¯F@@ßÑiSr| mÏÉÉ Œ1̝;»v킬,Kˆ4™™™áèÑ£HHH€³³3>ùä888ð‹t)))øî»ïð÷ßÃÉÉ »v킪ªj›ç°µµÅ’%Kx+V­Z…Û·o·žàéÕ«455‘ˆˆ\œzUÊ ùUXXˆ“'OòƒŽcTtQÎÎ΋Ř;w.vîÜIE!­`òäÉxÿý÷±}ûv888`ñâÅ=z4ß±H“““ƒï¿ÿgϞŠAƒ0kÖ,˜ššòšISSùùù`ŒµÙU‡ºÒÓÓqèÐ!(((Žyہ»»;ßH;GG}]ØÔ©S1fÌ|öÙg‹Å|Ç!€SRPPÀâŋqøða\Ÿ|S§Nítg‰ô•••áرc˜1c‚‚‚Я_?ÄÄÄ`íÚµŒü×8p`œ“BÚ?ºÐÅM›6 ²²²˜={6víÚ99úJÒTUU±téR|ˆQ£FaÕªUÐ××ç;Z³ÈÊÊB  33}úôá;!€š œÑ£GC^^3fÌÀУGŸ#‘.ÊÊʊް°)))žuëNž<‰ÌÌLTUUA,CKK ÆÆÆÜ£_¿~PSSã9}óš©©! ‹-BLL ŠM›†~ýúáœ÷Þã®ÜŸ}ßÿ=äååùŽKZš¢¢III8~ü8ÒÓÓ1zôhøûûC ðíµ8::"66Ÿ}ößQ!Í@‘`mm/ŸøØ»w/ øŽDº8MMMŒ9#GŽ|éœÂÂBäºîÔzñdWÝ›j óº7t+++£¬¬Œ{ž““#ñÿ„©©)²³³Ìה„H€ÉÜÜÜЯ_?LŸ>aaa022â;!íNC7&àþýûxðà~þùg„‡‡£€€   ###˜˜˜@II rrrÈÈÈÀš5k°wï^xyyaåʕmœ;„4‰™™ÒÓÓQQQѬ³ò|‘‘©©)>ŒáÇÃÖÖB¡?ýô“To°×××ǃž+©©©#ëÕ7"Ykä €>TV’fÚÚÚžzõ*ž>} àùL±™™™ØŽiú÷ž8ž÷‚ú999!**ŠïM& 1räH9róæÍÃìÙ³qûömˆD"\¹r¯µýiÓŠÁÏÏYYYÈÊʂŸŸŸÄœtÚÚÚž}û¶Ä:­‘ƒúÐÒlúúúˆŽŽFPPΟ?5kÖtŠ™Y ᓂ‚ÌÍÍannŽääd”——TTT ¡¡555èêêr¯“–£ñ×[ÇŽiÓàääv;߯‹fϞe˖áʕ+••…³³3222`aaÁMFØR+W®D@@Þ~ûmϯ¢¯X±‚{?00ÖÖÖ(**⟏ŸŸŸRÏAH}è&`òZbccñí·ß"<<ŒÃÏhIH{±yófüðÃ9r$Ə·ß~›ö“n~}|]áÆÌ¯¿þoœõÞÿýVm§%7w%]á»F^u"¯ÅÁÁ7nÄÇŒ³gÏò‡NañâÅžxñ"ŸþúkØÚÚŸö˜ÿ222ÜC[[“'OÆœ{÷€”ömÔמžžŒœœ‘ŸŸßä<ÅÅÅX¶lúõëeeeôéÓ®®®žpá‚T3·DG9³Í—¹sçb×®]|Ç „4‚ òÚLMM…žž8 ¢¢‚ïH„Ԏ~rçÎ 2ÞÞÞmÒÞµk×PVV† 49»»;òó󑘘ˆÂÂB$%%ÁÑÑK–,iÕÌäõ©««ÃÔÔ¿ÿþ;ßQ!  €H…¢¢"BBB0~üx888௿þâ;!€ZZZð÷÷—ø7z÷î]L:ZZZÐÐЀ³³3þý÷_Ï ü[·nqË8p€ûû͛7affÖ`{ ÃÏ?ÿÜä<¿þú+6nÜ333(((ÀÐОžžž|ù2·LEE.\@===,\žPâäCuu5V­Z…Ÿ}ûB  <<ŒÞö¯]»nøJ±XŒ5kÖÀÈÈ={öÄG}„ÒÒRÿ;û_{õ‚ÔoÑ¢E¯üŒ !íDªlmmqäÈlÞŒ›7o†X,æ;!€Ž‚‚|ýõ×xóÍ7¹×\]]1þ|deeááÇ000À²eË&LÀùóçYYYðõõå†.ýõ×_1a„FÛl耹Ÿ<666˜3g.\ž 1®z]k×®EJJ ®\¹‚«W¯"99ëÖ­ãÞß°a’’’pæÌ\¿~7oÞ|i °³³Cxx8üüü[¶lÁ¹sçpæÌÜ¿UUUXµjp÷ Ô^œ õÓÕՅªª*RRRøŽByFH+9tè›2e »uëßQé4âââØÁƒ›µ‰‡@ `iii¯\Ÿžž˜õîݛ1ÆXll,swwgŒ1¶~ýzÖ«W/¶gÏÆcnnn,..Žk£n{µ²³³™‡‡óððhržââbÄ Ä™±±1ûüóÏYQQ·Œ±±1KIIážßžqƒ™˜˜pÏMMMٍ7^ùylÛ¶éëë³?þøCâ=sssöÏ?ÿpÏ=zÄúöí[ïŸ5—››[‹×íhrrr$~æÒ6sæLV^^ÞjÛïèºÒwŽ ] ­ÆÓÓ{÷îÅŠM›H÷Â#ÆÄb1îÝ»\¿~{ïêÕ«7nzöì ôèѹ¹¹€±cÇâÒ¥K€Ã‡cÿþýØ¿?àòåË3fLœí՞õ뭷Э[7lݺµÉyÔÕÕŒ¿þú ¥¥¥ˆ‹‹Ã“'OàééÉ-ÓØ,«YYY055}åç±yófx{{cذa¯×ÎÄZ÷&æÌÌÌWn‡ÔO ÀØØçΝã; !€TV¥££ƒˆˆŒ5 ÎÎÎtc!<ª;꧟~Êuåñðð€··7RSSQ]]‚‚ÔÔÔTUUallŒÈÈH())aÒ€Iš®®FLL LLL ªªZo[ìÿ»ÉäååáСCèÕ«W“óÔ%++ KKKlÙ²¿þú+÷zí,«µ^œeµOŸ>HMM}ågqþüy;v 7n”x]("--ËÏã>‹ÚÌ€i°iÓ&ê JH;DiöööøþûïqèÐ!,Y²€Þ_ô„¶QwT(--…ºº:TTTððáC̞=[bù‰'bñâŘ1c€çW÷|}}1qâÄVÉcee…C‡áÁƒš¬¬DZZ1räHnÆfYýðÃáëë‹àÑ£GøôÓO%ÚìÝ»7Ο?ˆˆ|õÕWÜëÍÄZßì­€~=zôÀÔ©S±wï^Ÿ£B^@3“6£®®Ž°°0\žp˜1c†Ä/lBHÛ©õ“O>ADD/^ 777|þùçøñǹe'L˜€•+WÂÃÃÀó+K–,iÒ À-ɳyóf„††"00ùùù°··Ç¡C‡žå›eÕßߥ¥¥=z4jjjžyëÒ××ǹsç0vìXTWW#((šÑ™X뛜•Œš··7œáìì mmíf¯_\\Œ+W® <©éŸþ 555©fî 222øŽ@Ú9š ˜ðB,ãСCˆÅêÕ«%F!„ŒÍÜquÕÙY“““†={ö4{Ý˗/c„ 𮮆H$â^—““Cߟ}ùÚåuFÊÊÊÐÓÓã;iÇè ᅬ¬,ŒŒŒ0iÒ$|õÕW‹ÅXœz5ÔÕÕùŽF!DŠ}}}ÄÆÆJt§j +++èééáîÝ»Ükªªª9r$¢££¡šš(íž„t tᕖ–6mÚL›6 7ÜBéøV¬X;vpÝx#‹qöìY̚5 †††•}~ž¢££ƒY³fáĉtðOÈk €Ž #FŒ@||<ÔÕÕ1eÊ$&&ò‰Bˆ”tïÞk׮Œ%K\îΝ;Xœz5pþüyaïÞœÐÖֆžžÂÂÂÒF© 錚 톬¬,\]]ñÃ?àæÍ›pqqArr2ß±!„HÁСCannŽïŸûNâõ¢¢"ìÞœ ­­-âãã±zõjôíÛ}úôÁ|€Ÿ~ú nnn<¥'€s¡{H»£¬¬Œ¥K—"77ÁÁÁ••ÅŠ+ ¯¯Ïw4B!¯áóÏ?ÇÔ©SñÖ[o!-- ‘‘‘(++ƒ»»;~øáÈËË×»^Kn &„Œ€ÝرcRRRàïï,]ºZZZ|G#„Ò·o߆Ǝ lÙ²…þO'„TvÏÒÒ‡Frr2üýý¡££ƒÀÀ@úB:€ììlüðÃ8uê,--áçç‡éÓ§c÷îÝtðOOš ÆÀ±oß>œ?ÞÞÞxïœ÷ð駟BYY™ïh„Ž©ôôt\»vï€™ÊÊÊøŽÐfÊÊʐ€žž8ÈËËcúôéX°`dddŠŠŠøûï¿‚E‹ñœ–®‡&#ÖñãDZsçNX[[cþüù4$érssñÝwßÑ,ސP(ìÔ³Ÿ‹Åb\ŒxGŽAnn.œœœ0uêÔOÒ|ú駘2e ìììÚ0)!„ ÒáýöÛoرcúô郀€ôìٓïH„Òeܺu ‘‘‘žrå F…?üºººMZ·ªª Ó§OÇòåËñÖ[oµrRBH-*H§‘””„­[·ÂÌÌ ‹-B¯^œøŽD!R^^Ÿÿþ{œ9s˜9s&,--[Ž­’’ž»»cçΝ …RNJ©€Óùý÷ß±uëVôèÑ .D¿~ýøŽD!^II bbb555L›6 cǎåfé}ÙÙ٘5k8)€%„4„ Òi=xðaaaÈÍÍŬY³`kkËw$BéPjjjpöìY9r………prr‚³³3TUU¥ÞVjj*|}}ñÝwßA[[[êÛ'„ü€Óû÷ߎ«W¯ÂËË ŽŽŽ“£°!äU®]»†ƒâÞœ{˜0aŠM›Ö&gæïÝ»???|÷Ýw4D(!­ˆ Òe”••áСCˆ…µµ5fϞM÷ BÈÿ»}û6Ž=Šëׯ㭷ÞÂ̙3abbÒæ9îܹƒE‹áСC4š!­„ Ò%]»v ;wî„H$§Ÿ~Š#Fð‰BÚ\VVŽ=ŠóçÏÃÜÜžžž4hß±póæM ""€ï8„t:T.-;;;wîÄßÿ gggž¹¹ÑÄb„Níɓ'øá‡pòäIèêêÂÝÝ£F’Ê͌ҔžžŽ¹sç"44ýû÷ç;! „š¬¬Dtt4"##!0kÖ, <˜ïX„"ÅÅňEbb"äääàáၠ& {÷î|GkPAA>üðCÂÚښï8„tTò‚¬¬,>|¿ÿþ;F ///hjjò‹Bš¥ŒŒ'OžD||<ž>} {{{žžž@EE…ïhÍRZZŠ>ú3gÎÄäɓùŽCH§@!¯PUU…„„ü÷¿ÿ…ŠŠ <==1f̘vw™œBjUTTà—_~Att4ŠŠŠàèèGGGš««óíµTWWÃßßœzõ²eË ##Ãw$B:4*i‚‚‚üðÃ8}ú4|||Úōr„RYY‰S§N!** ………˜8q"\\\:å:GŽAll,öìÙÓá‹BøD!͔œœŒ 55ãÆƒ««+RAiSµÝ{¢££Q\\ [[[899AOOïh­îúõë Dhh(ÌÍÍùŽCH‡D!-TSSƒÓ§OãØ±c(((À„ àââB“×BZEíATT?~ŒñãÇÃÝÝœKô¿èÑ£G˜;w.f̘©S§ò‡‡ B€ ¢¢?ÿü3¢¢¢ ‰0yòd|ðÁÐÐÐà;!€+++éS§Gµé¬ŒíX,FXX’““±uëVššš ªª ûöíÃ'Ÿ|ÂwlذóæÍã;!턎¢šš\ºt ǎÝ;w0xð`Lž<#FŒ Q,!œ¢¢"œÄÇ_ZGEEïŸûn[G%„„t0ÚÚÚpssƒ›› 55III@aa!ŽŽŽ0bÄX[[£_¿~<§%ÒrþüyDEEÁÊʊï(€Žªª*žžžp#y=yòOž}:€ç“뀥¥áÖ­[8sæ ¶mۆÒÒRÈÈÈÀÈÈxã70`ÀšššðŒ„òjááá>|8ƍ‡Ô»ŒX,ƺuëââb8::",, ***ÜÙýÚ?ë’MÙö¯¿þŠÜÜ\ôèÑ`hhCCCxzzJs7 ©„&«œ`llŒÉ“'s¯‹Åb€§§#%%§OŸFXXD"‘DaЯ_?˜˜˜@GG‡Ç=èZêv?ší^ÂÛäT-9Ûޞ»IÐՃŽMCC»v킗—.^Œyyù—–Ù²e Ν;‡3gÎ@CC ,ÀªU«ðÍ7߀1öÊï@S¶mccƒ9sæ`Á‚2d”””Ze? ©ÍLi5µ…Á­[·p÷î]€ŠŠrc€ËËË£oߟ055…‰‰ LMMa``@÷ŒBBB š5 h݃“üü|„††âÔ©SžxñbkÅlržŠš8q"„B!üýý%ºI„……I¥›ÄëÄ·t]š¥µiV®\‰èèhxxxÀÇÇGbp‚ם žîÏÎßßòòòX·nÝKï 0111èß¿? //ÇGzzúKË6wÛOŸ>ÅŠM›‡;wî@__ÎÎÎX¹r%wU ¥è;FÅ!„•••ìîÝ»ìĉ,,,Œ-\ž¹žž0æêêÊ/^̶nÝÊbccٟþÉ?~Ìwd^ÅÇdz4kÿ‹úô)SRRbŒ1VSSßüòKfhhÈ455™{öì™Äºáááì7Þ`JJJÌÒҒ%%%±ýû÷3333ŠššÈ¬¬¬ØíÛ·¹uÊË˙ŸŸÓÓÓcºººÌÏϏ•——sÛ«ûšÕX%%%VTTÔà~6Ô.cŒUUU±   & ™žžÛŸ}{œŸÑÕ«W™ŸŸ>Û²eK£Ù^µ?MáææÖ¬å»ªyóæ1LVV– fffÆ/^ÌRSSÙ̙3%~ÆÍU÷gVQQÁ† ’’’^zOII饟µ¬¬lœÛiî¶ëª©©a7oÞdÞÞÞlÒ€I-Þ¯Zô#¡.@„^ÈËËÃÌ̬Þî(b±™™™HOOGFFnÜž¬¬,üûï¿ÜÙ3uuu…BôéӇ{ôíÛÊÊÊmœ+BAABBBðæ›ohžkC­ÄÄDÄÄÄ@OO¡¡¡°··Ç˜1cðÓO?AWW¡¡¡˜;w.Ν;X»v-RRRpåÊÏÏÒ®[·_|ñÅ+»K4–£)Ý$j6l؀€€$œ9s***~i øè£°gÏ8884šíUûÓÑ<}ú555Üó²²2”——sÏ«««QRR"±NQQ‘Ä~—––¢²²’{^YY‰ÒÒRî9c EEEÛ())Auu5÷ŒŒŒeeeÜóššüý÷ߞÿ››  ÁöíÛ¥:Tq÷îÝqðàAžººŸtUI(⧟~‚¡¡aœë6vŲ¡m×%++ KKKlÙ²B¡°Ùû@HsQ BH‡TRR‚ŒŒ ddd 33YYYxøð!D"·L·nÝ ««‹^œzA__:::ÐÕՅ@ €ŽŽºwïÎãŒ¬ŠŠ)))8pàK﵎ P]/^„¡¡a“º6äää@ D"TTT››ËM'‰ ­­Í}æ&&&ˆ‡……àæÍ›pttDjj*·Íå4–£)Ý$k×ÌÌ ÑÑÑxã7êýŒ¶mۆuëÖ!&&Æ kr¶–/ÝZ\\ ±XÌ=‰DšššàžwëÖMâ@ýÅçMYŠŸu455%ž«ªªJôWWPP(ªeee_Ꞣ®®Žnݺqϕ””$f—““ƒšššÄ:ßO‰Ý»wÇþó®”••…@ @ïÞœáëë‹'N ""B*]€j…††âúõë8pà÷^hh(°uëVôíÛ)))X³f bcc:::8þŒÄϳ©Û¶²²ÂgŸ}kkkôîÝÙÙÙØ°a222püøñíW-êDCW!’ššÚKC—ŸšººOž<ÁãǏ‘““ƒÇãÖ­[ÈÍÍœ'OPQQñÒAHϞ=%ZZZÏÕÕÕ[mŸ²³³acc8::â³Ï>{í3Œ10Æpÿþ}øøøàúõë044ÄÇann.±¬¬¬¬ÄóڃÜA`ݙ£•••%ÎÚæääÀØØ˜{njjŠìììó5–C]]ÁÁÁ†X,ÆíÛ·±qãFxzz"!!¡IífeeÁÔÔô•6oÞ ooo‰ƒÿŠdk©îÝ»cΜ9ÜshhhH,£ŠŠ9¹®ý+ZAAšššèÓ§ŒŒŒðÑGqËÏ?ÿ,õö,X;;;‰×|}}!++ gggdddÀÂÂAAAÜû°¶¶~éªHS¶œyóf„††"00ùùù°··Ç¡C‡€»c„Ôƒ®BÈÿ{öì žGaa!òóó%^{úôé+×WWW‡ªª*TUU¡ŠŠ šªª¢{÷îÜYÒ=z@VV–;Zû§ŠŠ&îß¿#F //²²²ÐÓÓC¯^œðÑGA__"‘šÅ7@FF†Ž»wïbذavmxñ×Cc¯5v&^VVVâ,7˜››7˜£>EEE …ÜÏ¢±vûõ뇹ššW^ÈÌÌĘ1c0gÎøûû79[}ûÓtv¶i233QQQQoñöº7wvô#éÚ§!€ŽÚƒ÷–öÁ}úô)ž={†gϞ¡€€ÅÅÅxöì***••…šš®Ïum—Ú?‹ŠŠ ‹¹ƒi±XŒœœäää`éÒ¥PTTÄúõë_kÿ„B!Fމ#GŽ`ÞŒy˜={ö+»6ŽÄŽiÓàçç‡ýû÷üüü0mÚ4î}mmmÜŸ}[¢»Dc9^ÕMbäȑMn÷Ã?„¯¯/""" ¬¬Œàà`„‡‡sï÷îÝçÏŸÇØ±cQ]]e˖5)[}ûC€§OŸ>|G €Ó¢€B€D]]ýµºÝžqcǎðŒ;’®®.Œ1þ|hðêCS͞=˖-Õ+WìÚÐ+W®D@@Þ~ûm€››V¬XÁœ__w‰ÆºX4¥›Dcíúûû£ŽŽ£GFMM V­ZõRv}}}œ;wŽ+‚‚‚€ÖýƒBÚêD!íDJJ Ǝ +++|úé§7n׌%7“ö‰ºgŒ>êÔ0úŽ‘ÆÐBi',--‘——Çw B!Üëg@!D*"##1dÈ,X°ÇG~~>ߑ!„tBt€BÚ 999Üžqׯ_ÇÞœ{ѳgOšªªB ÀÀÀãÆã;"!„N€ Bá‘H$BZZÒÒÒpíÚ5ôèÑùùù(++Cvv6äääðìÙ3hjjJe zB!„ BiEUUUÈÈÈàòÓÒҐ‘‘ÊÊJÏ'8;åú]IDATÓ222‚‘‘F…x~5ÀÀÀ|ð‚ƒƒñÛo¿¡  €Ï]!„ÒIP@!¯©°°<xx~ / ! ¯¯ 0€›Y÷Eb±}úô‹‹ Ÿøâ‹Vy˜Žª{÷îðôô€«b¯––ÆwÒÎQ@!(++Cnn®Ä~vv6***PUUÃØØ¶¶¶055E=ZÔÖ¶mÛ`kkKþ„4`ïÞœ|G €C£€ÒåUTT ;;[â?33UUU`ŒAII úúúùFFF‘‘‘zggg©o“B©‹ Ò%œ8qÁÁÁ4µü+Üœ{»víÂðáÃùŽÒjêŠ#++ CCC‰üŸ}û¢[·n<§þ###}ÓŠMkѺ[¶lÁ¹sçpæÌÜ¿UUUXµj•Ä2QQQHLLÄãǏagg‡¹sçøß?c¬ÓÜS@!„¶G3“.Aš3ËËË£€€ŠŠŠ¯\ŠîYÿº0`bbbп@^^†ŽôôtnÙÜÜ\èééD"޵µ!‰^ږ4u†™€ !„Ò4ÔˆfÒÒÒBNNŒ›œîÇann.ñڋ7ÐÖü€²²2ÊÊÊZ”B!€Ôˆf7nŽ=Ú¢u…B!ÒÒÒžn<Œ1ÔÔÔ4yýÚþÿ„B!-E!ÍŽzõjlݺ!!!ÈÊÊÂÓ§O‘””;;»Fם7ofϞÛ·oC$áʕ+ppphrÛÚÚÚž}ûöëÄ'„BHG]€i&333œ9s+V¬@pp0jjj0hÐ ,_ŸŒÑu}}}!++ gggdddÀÂÂAAAMn;00ÖÖÖ(**¢ !„Ò"t0é€ypgD7B!]u"„B!€ ¡€B!„.„ B!„Bº*!„BéBš „B!€ ¡€B!„.„ B!„Bº*!„BéBh&`Ò%šššà‹/ŸÀ?þÈw”v)55ãǏç;!„BÚÍL!„BHB]€!„BéBš „B!€ ¡€B!„.D@!ß!!„B!mãÿ%fÛoa9¹IEND®B`‚pyroute2-0.4.21/docs/html/_images/inheritance-e3c5704fb116d5e1ca6c2d8505ff63c892fa04f2.png.map0000664000175000017500000000256113152333364030101 0ustar peetpeet00000000000000 pyroute2-0.4.21/docs/html/_images/inheritance-019a4d34bee1d6abc5b8dde68c0a419d26e0d6e9.png0000664000175000017500000002354313152333364027547 0ustar peetpeet00000000000000‰PNG  IHDR¬é÷H”bKGDÿÿÿ œ§“ IDATxœíÝy\gþð‰È‘€ˆ€"`@RŠˆÕ âU) (§”JkV,í¯VÅ£bÕ]ëAÜJ-Š rˆ  «Ôj»J5€€« W@@»xñb|1}}ýŽŽ4.—[^^€7îØ±ÃÕÕµ¡¡¡¡¡ÁÙÙy˜»nJJJJJÊpAr!:::..îÕ«Wœ‘oz@àææ–””„/#l”ü.ñ Î_…Ba±Xø4“Éìý—ÐØØˆOs8"‘ˆO›™™1™Ì>`&쀬¬ŒB¡ÛÙl¶°“ú$“É111µµµœû455-//ǧY,ÁØÙÙ)++ØÛÛ'''s¹\lú‚‚‚yóæõôôÞ)‚Þ%ÞýUpSU…áçç7äµSD"ñŋjjj†uttà{!†aÂ_válïå{ÿ”Ëå ;™4i~Íò@ô™œÿþÞœ{oݺ¥­­}ìØ1WWW|E/_ŸTUUílp©©©÷îݳ³³rI¹±ÿþÒÒR|šH$Nž<ÙÜÜðk×® þB±nzkkëØØXGGÇ>J~—x÷¯Î ‚¿044¬®®Æ§Ÿ—ËÍÉÉÑ××Ç·oß.<äââ2Ì]‡‡8,ÖMßoƒtÞgV„»œThÃùKèèèX¿~œ¶¶¶©©)þ!Þ>ÐÞÉçó·mÛ6mÚ4}}ýžž8áOOœ8abb¢­­M§Ó;::ï€ÏlzzúìÙ³‰D¢Mqq1ÞÈår?ûì³I“&™˜˜>|XYYy8oY‹àš?ëŠï·‡A:ï3+Â]Š BÎ_‚HˆûãÁƒ&&&ÃYR‹`¿äfÓdø»Ä»¿ 8'dƒÁhjjª©©‰ŒŒ€ÑhšãôD²KÀMUÌ˜>}úܹsù|>•JŽŽF '’]Š =xŸw!ŽžšˆiÓD$»ŒåÓÝ»wkkk544”••‰D¢šššŠŠŠ¢=e €á€"(Ÿöíۗ­ªªª€€4~üøñãÇóx< Ô¹:p8,ŸÖ¯_¯¡¡ÁãñÚÛÛ_¿~­¬¬ŒnݺE‹¡Î€Ô‘ ŒyðàArrr[[›®®îÛ·o ‚±±ñéÓ§—.]úîÃPåDmmmFFÆõë׍CBBlmm—-[öìÙ3›¬¬,]]] ÃÔÔÔ|}}Q'•šúúú¯¿þu ôJKKmÓä÷ßïÓw‘‘mMMM)))EEEÆÆÆóçÏ^š~åʕ۷oïÝ»î? À  Ê€îîî«W¯ž;wŽH$úûû/[¶LII u(dAÃf³ÿõ¯-^Œxݺu:::š Û ʆžžžâââ³gÏvuu…††âw:ŒAi÷üùóžžž;wî¬X±"00PSSu"ä AéU]]S]]ý駟zxx Ž€|‚¯ÈH£û÷ïÇÅÅuwwoÞŒyæÌ™šã Ï`$(Ezzz._Ÿœ0kÖ¬ððp===ԉ0”999±±±ø³YÕÕÕQÇ@Q@Dï—_~9tèÐŒyó.]ºD$QÇ@±@D©¬¬ì»ïŸÓÓÓûñÇµŽŽPÇ@ADãéÓ§ß}÷]WW×þýû§NŠ:Š Š €œzõjϞ=¯_¿Þ¹s§‘‘ê8(:(‚’#/\ž°cÇÔq7U•˜ÒÒROOÏ/^dggC@zÀHPìÞŒy³}ûöžžž3gÎhkk£Žø  ŠW^^ž———··÷ñãÇ¡ …`$(./^ŒˆŠŠÒÐÐÈÍÍUUUEÐ?(‚b‘œœ|þüùÀ•¿H98±ÖÖÖ   ÆÆÆ¬¬,š€H? ŠR~~þÑ£G80{ölÔYÃEP4ÚÛÛ£¢¢TTT²²²TTTPÇ ‹À;wŒœœ}}}÷ïßÙ#Á1111L&3--MCCuœ‘ îèè@Bôìíí ê@f@œæææ7zxxÄÇÇ£Î2</%%u ÑóóóCÈ(‚£TTTtôèÑ#GŽ˜™™¡Î=(‚#ÖÝÝœk×®ŽŽŽK—.Á@d|022---~~~³gÏ>tèT@äŒG €€d÷îÝßÿýûᅬ: @4 ×?üp÷îÝ˗/“H$ÔY€O.2 ‡‡Æáp‚ƒƒ ?þ9#Á!ÔÕÕmذaÛ¶móçÏGEºÀÐÈ(‚ƒ¹yóæNž< @^Ááð€~øá‡„„„K—.)xìé鉎Ž611ÑÖÖ åp8†ü_|—˜˜8sæLuuu{{û²²2Œ‘@ ÄÅÅ͚5‹D"YYY•””$$$˜››‰D{{ûŠŠ |±¢¢"[[[‰dii™œœŒ7òx<:®££C¡Pbcc{¯ Q"؏ÎÎN:ÎçóOŸ> OC?zôè7Š‹‹ŸÐbd29&&ж¶¶wŸŠŠŠåååø4‹Åæî:Ì÷F‚Q__ïïï¿uëÖ+V Î"-jjj,,,ð#ß©S§ÖÕÕ Ž€ð)ò$‰Ëå Ûõõõ…í-–™™Y\\lcccnnž——‡7²Ùl|ÚÔÔT”ï €ÿ‚"ø?¿þú맟~zòäIÔY€ˆ‘‘Quuµð¿ÍîînŒ]Žgèlmm322ZZZŽ9Š7TWWãÓUUU"\BPÿ”žž~ðàÁŽŽ42™Œ:‹t¡Óéaaaåååííí¿ýö•JÅÛuuuËËËEµVZZÊãñ°^å5 22’Íf³ÙìÈÈHQ­ €Þà+2†aǎ«©©IIIQRRBEêlÜžqÜžqÞÞÞµµµ–––QQQxûÖ­[^Ÿ|)Å·CBBÖ®]ûèÑ# ‹€€$ŒqǎVVVZZZ7n,..ûŠèCÑ¿ïÚÕÕinnŽ:‹€ùùù]ŒxuŠázøð!FÎA±lœ/€œB¿yófõêÕÎÎÎ XeƒÁhjjª©©‰ŒŒ€Ñhšã9€ž‡Ã ëÖ­ƒ'ÃI¹éӧϝ;—ÏçS©ÔèèhÔq€RÐ"XUUE§Óccc) ê,`0šSyЈ‡Ã÷îÝ OLL„ Pž‘à7Ž=záÂMMMÔYè)VÌÊʺpá… ÔÔÔPg‘œÊÊÊ­[·Nœ8QCCC]]}òäÉjjjzzzrùŒMFJŠà™3gJJJǏW wa‰Dº}ûvss³°EIIiêÔ©pa ˜âÁƒŸ|ùòÔ©Sšƒ  ¥¥¥©©),‚::: ,8þŒ““Sjj*Úl 'ÿ_–_}õ•žžÞæÍ›Qg‘šúúúœœœÂÂBUUU.—›‘‘aØŽiÓöíÛŒaXvv¶\Ϙ1cΜ9šS™!çE°»»ûóÏ?ÿ裏Qg‘‹•““s÷î]MMMwww777uuõû÷ï/]º”B¡dff*ø=bèCž‹ ^ÝÝÝœŒŒPg¯îîî_~ù%''§²²òœ÷Þ[±bł zßåE œ‹ ŸÏ_»víš5k>þøcÔYDïéÓ§ÅÅŝT*ÕÓÓSKK u(d•A·f͚uëÖ-[¶ uQb±X©©©L&sòäÉîîî...***šC óä­¶··¯^œ:""bɒ%š³ˆ@GGGIIIvvvEE…Í»'ûc$WEÃáù嗲þ ôçϟ_»víùóç‹-Z¹råŽiÓP‡@>ÉO|ûömppðÖ­[ç͛‡:Ë(UUUeggߟ}[IIiùòå4 .p@Ü䀟~ýzõêÕÑÑÑ2wsÀžžžÒÒÒììì{÷šzxx,^ŒXYYu.…<ÁöööÀÀÀšš(;;;ÔY†‹Ëå^¹r¥®®îƒ>ððð¡ðÈ™/‚ø'!Û¶m›;w.ê,CkiiÉËË+**âóùK—.õðð>“€„lA.—»zõꯟúêÃ?De0øÉŸ7nLœ8ÑÝÝÝÕÕUCCu(†ÉtäóùAAA6lXŽhê,ý^ÊöðáÃ3føøø888Œ§ˆ·ò@šÉjäóùÁÁÁëׯ—¶ïr8œâââôôôÖÖVGGG*•jaa:`@2Y;;;ƒƒƒÃÂÂ>úè#ÔYþTSS“ŸŸ_\\ÜÕÕåììL£ÑŠL™‚:`h²W»»»×¬YóÉ'Ÿžžž Îòç}«>|8aÂwwwgggUUUÔ¡# cEP |öÙgï #Œ”­²²rΜ9p)2MƊà—_~iii¹víZɯº­­­šš(//ïÙ³gÎÎÎÞÞÞd2Yò1¢%KEp÷îÝ'Nd0’\iuuõå˗oߟ=~üx*•:qâDIˆ•ÌÁãǏ¿~ýzÛ¶mX—ðR6‹¥­­œbŊå˗ålÈ%Ù(‚‰‰‰÷ïß?räˆX×ÂårþùçÜÜÜǏÛÙÙÁ¥l((‚YYYÙÙÙ§NӇ­­­W®\)**zýúõ’%K||| ı"€ê[¥ð1ŒEEEK—.ãµd2¹ÏMñKÙ®_¿®¥¥åîîŸ|ùò &Œ-©Œ‘Âm-ïnkÑ·:88Hø“ÉHKK»xñ¢ðR6&“iffæáá±dÉ…}›|okÔ)€Ìèû÷O&“}||D«Ó§O‡„„ŒyóÆÑÑqݺufffš¡'¯Û:-- u Ke€ŠŠvøðaÔAÒEQnj¢¢¢ð.E)‚Ð/(‚…E î äA0„a~ŸµµÕÈÈ*&9PhìÚµ‹N§£NÀˆ‰¥„žžžY³f‘H$++«’’’„„sss"‘hoo_QQ/VTTdkkK"‘,--“““ñFG§Óutt(Jll,Œ,nK¡PtuuÃÃÃù|Ÿp™ŠŠŠüüü>_œ†ÝÈqsss333[ZZÝÜÜ222®^œÚÚÚêáá±~ýz|™   íÛ··µµ¥§§çääàÑÑÑ555L&óÖ­[YYYbЧhLMMsrrºººÆÒOFFÆÍ›7ËÃnd€à¯|}}c†a›ÍƧ9†aÂY"‘ˆO“É䘘˜ÚÚÚÞ¯555-//ǧY,Ö» GG$ïKv™ššbŠ®®nllììì\PPÐÓÓ3Ì߉p`Æb±ðé²²2 …‚Ơ7¯§§§÷Ât»‚ok0R}¯öóóûu—Â_ºhöþýû{÷îœu떶¶ö±cÇ\]]1 #‰/_ŸÄŸÔÑÑсÿ©Œ1†aNNNÚÚÚcïGFݹs§®®N8«¡¡¡ªªjccsíÚµ!_+Ü^Ëåâ܎ŽŽI“&q¹\ ì­­ccc±¿nkT»Höa @úEQ‡?ÛÓӓ““3uêT|F‚‏±ÿ©Tê͛7G1üã?ði‹% ŽS¡Ú |[ƒ‘Bùé0F+--åñxX¯³ï‘‘‘l6›ÍfGFF"Œ'O8ޱ±±››Û¥K—ž}útaaá®]»êëëíìì<<<ìììPç Ö÷ ÁÁÁšÒŒëùóç$‰H$ޱŸ¥K— oùÙÓÓSZZšÍb± <<</^¬€×$HÛ¶•ÞÛ€!õ-‚ÒŠ¡¡!,,ìܹsbºLUUUvvöí۷ǏïââB£Ñ455ű"€t’ö"ˆaØü±e˖ÔÔTuuuñ­åùóçÅÅÅ×®]{þüù¢E‹V®\9mÚ4ñ­ %d bv÷îÝ#GŽœ;wnüx±ŸÄìèè())ÉÎÎ®šš°··÷ðð°µµ…Û» ¯d£b–›››––vúôiIÖ#‹•ššÊd2'Ožìîîîâ⢢¢"±µ$@fŠ †açΝ+//ß·oŸäWýôéӂ‚‚ëׯ㗲zzzjiiI>@äd©bvàÀUUÕ>O5“€/^æåå577/\žÐËËËÜÜUÀØÉXÄ0lóæÍsæÌ B£»»û—_~IMM}ôèÑìÙ³W¬X±`Á8u€Ì‘œ"(6lذdÉ___ÔYþÄb±rrrîÞœ«©©¹råJgggü Aé'{E𞞞ÐÐP___777ÔYþ¢¶¶öêÕ«EEEÝÝÝÎÎÎT*UOOu(À`d²bÖÙÙD§Ó/^Œ:K?ÚÛۋŠŠòòòž={6oÞ<*•jaa: ²Z1 ãóùAAA[¶l™;w.ê,ÂOæää<|øpƌ>>>ãÆ)Ê}+~2\1 koo÷÷÷ß³g ê,Cc2™—/_Ÿ{÷®ŸŸŸ‡‡Ç²eËÆ~M4`Œd»böêÕ«€€€Ã‡ËÐñ&›ÍÎÉÉ),,TRRúøãW¬X1eÊÔ¡PP2_1 kii þç?ÿidd„:ËÈp¹ÜÂÂÂ+W®ÀÝœ@EŠ †al6ûÓO?•Å:ˆë}w/CCÃ+V(æÝœ<9)‚†={ölíÚµ'Nœ011AeLúÜ݋J¥Nœ8u(ä–üALŽê ®­­Mø=gggooo2™Œ:òF®Š &wu'Œ»Weeåœ9sà=DHފ &§uP¿DïÁƒšššîîîý^¢×ÜÜ 70LrX1y¯ƒ8üœâââ®®®>—èYZZŠŠŠÂýŸ’|AL1ê îÕ«WW¯^ÍÍÍåp8ŽŽŽÞÞލÆÆÆ ÒyY!ÒCn‹ &ûߛ©®®®›7oŠ€€ÄÇÇwwwc6uêT??¿Ã‡Åz Dž‹ öß:xìØ1Źõéܹsÿýï g‰Dâ¬Y³222 Š@jÉyÄ0ìŋkÖ¬Ù³gϜ9sPgùsss1åÉÍÍURR"aÜžqø„‘‘‘™™™8VúE .^Œˆ:±?Œ ¹I“&%%%­Y³&22ÒÁÁuœ?ÙØØÀ‰óóóC —Bœ*ÒÐÐHJJ:qâDAAê,é¢EÃ0UU՟~ú)11ñÒ¥K𳀈¢A ÃTTT úé'ÔYÒBŠ †aJJJ±±±åååGEEdd÷ú¹Ñ%Óû•Ý_##Å*‚†„¿ÿýï<oëÖ­š³ÐSž"ˆûꫯ ¶lÙÒÓӃ:‹„޶¶Áx€>ŽböÅ_888µ··£Î" »ví¢ÓéšS u·bæííœqãF??¿ææfÔYúÑgÔ&œåóù6lÐÑÑ133‹‹‹ëœ@ll,…BÑÕÕ çóùÂUTTäçç3Œá¬ŽßNŠŠŠlmmI$’¥¥err2ÞØÓÓmbb¢­­ÊápF—œÇãmÚŽI__êÔ©›6mâñxƒ‡LLLœ9sŠºººœœ}YYÙàa=zŽjÕ*---ooïÖÖÖÁà òŠâããÉd2\ƒ(o ïñãÇ˗/¯ššäJ}}}‡\ŠÏÖÎîÚµËÉÉ©ŸŸŸ®®néÒ¥Âv Ãðöúúz''§Ý»w _ëææ–””ônŸý®ŽßNôõõÓÒÒž\nyyy@@ÞøÝwß-[¶¬ªªª­­í“O>‰ŒŒ]òšš(''§ºº:Œ}çΝƒ'€ÑhÕÕÕoߟݳgς cmm]\\ÜÞÞþòåËðððuëÖ f7E¥Rÿ↳}”€"(­­­žžž7oޔØÇR) ‹Å§™Lfï"(l/++£P(øtAAÁŒyózzzÞí³ß•öÛ ™Lމ‰©­­íœ°………ð?ŠŠ&ccãÑ%755íÝ.\é@ ñi‡C$ÓÛ«W¯ŠM›6x˜AÞÔÓ§O ÖAEðO\.788øüùó’YÝXŠ šš—Ëŧ¹\nï"Ø»]MM Ÿž5kÖ­[·úí³ß•öÛÉœ{÷h4šŽŽÎŒ3®\¹‚7öynòžqãF—ŒO»p¥%ìwv 0¿ýöÛG}4iÒ$Œ]IIið0ƒŒ)ü?’á€"(CàìÆŸÔÔÔ;øzûí„F£•––âYkN +//oooÿí·ßšTêè’DDDà'"#""Fñë( ‡ÃÑÔÔTWW¯©© 2Ì@ý¹%摊LJKK xûö­øV1œÃ%&“9þ|‰ôþûïŸ={Vž±:::Ö¯_¯­­mjjЬ‰·c†ßI[[[›N§wttŒÛç[| NÒÓÓgϞM$mllŠ‹‹ñÆîî ‰ôÁdee.9—ËÝžq£žžžžžÞƍ…G©%ìwv 0¹¹¹ïœ÷ž²²²‘‘QLL̐aêgD,p8,Cäÿ~‚£s÷îÝ={öÄÅÅ‹£???‘ßJ‹@ÁÖI'@Ûˆ ÷ïÃ?ŒÿÛßþvýúuÔYbEp@éééIIIßÿ=ê,¢GèêP!ý |€"8UUÕøøx.—ûÅ_tvv¢Ž3„Æö{rd€ˆÕ@ -(‚Cû¿ÿû?WWו+W²ÙlÔY"EpX\]]Oœ8ñÙgŸ¡Î%(‚ÃE&“/]º”––vðàA8.@n@UUÕžž8mmíàÄD} IDATàà7oÞ Ž(‚#áããsïÞ=ÔYcߌ¥7oÞlÜžqúôé;wîÅ æÄ÷ðu ˆDâ™3gP§ÃEpLΞ=›——wüøqÔY£Ep¬X,֖-[ŸþúëE‹¡Î1(‚"Àår·nÝJ$÷ìÙ£ªªŠ:`àƒ ‰ÇŽsrr¢R©¥¥¥šãFF‚¢ôêÕ«íÛ·ëêêFEE)))¡ŽŒEiâĉǏ·°°ðòòª¬¬D04 ŠÅ³gÏð!á7ß|£¢¢‚:`@0 ==œøøxGGG//¯_ýuÀ€`$(^/_Ÿüúë¯544vïÞ­®®Ž: / Š—––V\\œ»»ûªU«RSSQÇôEP–,Y’Íf³i4ZYYê8€ÿÃa‰jjjÚ»w¯ªªêîÝ»555QÇ@DáöíÛû÷ïwuu SVVF…‡Ã888\Ÿ|yòäÉnnn?üðCOOêD(.(‚hŸ+W®tvvzzzæææÂ$àp=‡ŸŸŸ¿víÚU«VÁƒ%$(‚Ò‚Ëåž:u*???((hÕªUãǏG…EPºðxŒää䎎Že˖­[·>A@Ü J#@ŸŸ?}út:N¡PP'@nA”jL&óäɓ---ÁÁÁnnn£x˜ `pPeÀëׯ¯\¹2þü   éÓ§£N€ü€"(KîÝ»wöìÙGyyyL˜0u"dAÙÃãñ RSS»»»ýýý]]]á£dF Š  {öìÙùóç ­¬¬­­­‡ùÂßÿýÛo¿k6äRRRPG²Š ÷žÎòùü 6èè蘙™ÅÅÅõ^ 66–B¡èêꆇ‡óù|a{\\ܬY³H$’••UIIIBB‚¹¹9‘HŽ··¯ššÀ+**²µµ%‘H–––ÉÉÉx#Ç£Óé::: %66n» DŠ ±o¿ýöÑ£G>ŒqãFŸ'ÊgddÜŒyóÁƒ•••û÷ï¶çææfff¶ŽŽº¹¹edd\œzµµµÕÃÃcýúõø2AAAÛ·ookkKOOÏÉÉÁ£££kjj˜Læ­[·²²²$öœ’’’’’2äb}vá,…Ba±Xø4“ɶc&l/++£P(Âv6›Os8 Ã…³D"Ÿ&“É111µµµœWjjjZ^^ŽO³X¬aŸŸÃY @#A0b ŠŠŠøŽ™™Yï Û)JCCƒ°]__ŸÀ¯Û›:uªp–Ëåâә™™ÅÅÅ666æææyyyx#›Í611éÓ9"E HMM­œœŸnjj¶VWWãÓOž<éýa{UU•¡¡áˆVgkk›‘‘ÑÒÒräȑÐÐPŒÑÀÀ wŸ# Š Ý¡C‡8NuuõçŸ.l d0 ›6mêýa;ƒÁXœzõˆVG£ÑJKKy<ÖëC˜€€€ÈÈH6›Íf³###Çüžè Š Ðɓ'óòòŠL™âîîŸjÕ*aûöíÛMLL¬­­-ZŽråÊÞ/ñôô\žp¡µµ5…BÙ¶mۈV²víZmmíššš€€$Œqǎd2ÙÊÊÊÑÑÑÅÅEYYyìï €Þà‚'E$Š»Èˆûú¹‡ÒhŽáà ÀðÁHH;ƒÑÔÔTSSI£ÑPÇò®ÒnúôésçÎåóùT*5::u o ‘Ó±pDDDDD„8zƒÃa€‚ƒ"PhP Š @¡Á# êΝ;š#ˆQ]]ê@fÀ—¥Qkkëõë×Q§#555Ô)€l€"PhpN Ð A€BûP?b‘Š °IEND®B`‚pyroute2-0.4.21/docs/html/netlink.html0000664000175000017500000011017413152333365017543 0ustar peetpeet00000000000000 Netlink — pyroute2 0.4.21 documentation
pyroute2-0.4.21/docs/html/devgeneral.html0000664000175000017500000002751613152333365020222 0ustar peetpeet00000000000000 Modules layout — pyroute2 0.4.21 documentation

It is easy to start to develop with pyroute2. In the simplest case one just uses the library as is, and please do not forget to file issues, bugs and feature requests to the project github page.

If something should be changed in the library itself, and you think you can do it, this document should help a bit.

Modules layout¶

The library consists of several significant parts, and every part has its own functionality:

NetlinkSocket: connects the library to the OS
  ↑       ↑
  |       |
  |       ↓
  |     Marshal ←—→ Message classes
  |
  |
  ↓
NL utility classes: more or less user-friendly API

NetlinkSocket and Marshal: Base netlink socket and marshal

NetlinkSocket¶

Notice, that it is possible to use a custom base class instead of socket.socket. Thus, one can transparently port this library to any different transport, or to use it with eventlet library, that is not happy with socket.socket objects, and so on.

Marshal¶

A custom marshalling class can be required, if the protocol uses some different marshalling algo from usual netlink. Otherwise it is enough to use register_policy method of the NetlinkSocket:

# somewhere in a custom netlink class

# dict key: message id, int
# dict value: message class
policy = {IPSET_CMD_PROTOCOL: ipset_msg,
          IPSET_CMD_LIST: ipset_msg}

def __init__(self, ...):
    ...
    self.register_policy(policy)

But if just matching is not enough, refer to the Marshal implementation. It is possible, e.g., to define the custom fix_message method to be run on every message, etc. A sample of such custom marshal can be found in the RTNL implementation: pyroute2.netlink.rtnl.

Messages¶

All the message classes hierarchy is built on the simple fact that the netlink message structure is recursive in that or other way.

A usual way to implement messages is described in the netlink docs: Netlink.

The core module, pyroute2.netlink, provides base classes nlmsg and nla, as well as some other (genlmsg), and basic NLA types: uint32, be32, ip4addr, l2addr etc.

One of the NLA types, hex, can be used to dump the NLA structure in the hex format – it is useful for development.

NL utility classes¶

They are based on different netlink sockets, such as IPRsocket (RTNL), NL80211 (wireless), or just NetlinkSocket – be it generic netlink or nfnetlink (see taskstats and ipset).

Primarily, pyroute2 is a netlink framework, so basic classes and low-level utilities are intended to return parsed netlink messages, not some user-friendly output. So be not surprised.

But user-friendly modules are also possible and partly provided, such as IPDB.

A list of low-level utility classes:

  • IPRoute [pyroute2.iproute], RTNL utility like ip/tc
  • IPSet [pyroute2.ipset], manipulate IP sets
  • IW [pyroute2.iwutil], basic nl80211 support
  • NetNS [pyroute2.netns], netns-enabled IPRoute
  • TaskStats [pyroute2.netlink.taskstats], taskstats utility

High-level utilities:

  • IPDB [pyroute2.ipdb], async IP database

Deferred imports¶

The file pyroute2/__init__.py is a proxy for some modules, thus providing a fixed import address, like:

from pyroute2 import IPRoute
ipr = IPRoute()
...
ipr.close()

But not only. Actually, pyroute2/__init__.py exports not classes and modules, but proxy objects, that load the actual code in the runtime. The rationale is simple: in that way we provide a possibility to use a custom base classes, see examples/custom_socket_base.py.

Protocol debugging¶

The simplest way to start with some netlink protocol is to use a reference implementation. Lets say we wrote the ipset_msg class using the kernel code, and want to check how it works. So the ipset(8) utility will be used as a reference implementation:

$ sudo strace -e trace=network -f -x -s 4096 ipset list
socket(PF_NETLINK, SOCK_RAW, NETLINK_NETFILTER) = 3
bind(3, {sa_family=AF_NETLINK, pid=0, groups=00000000}, 12) = 0
getsockname(3, {sa_family=AF_NETLINK, pid=7009, groups=00000000}, [12]) = 0
sendto(3, "\x1c\x00\x00\x00\x01\x06\x01\x00\xe3\x95\...
recvmsg(3, {msg_name(12)={sa_family=AF_NETLINK, pid=0, groups=00000000},
    msg_iov(1)=[{"\x1c\x00\x00\x00\x01\x06\x00\x00\xe3\...
sendto(3, "\x1c\x00\x00\x00\x07\x06\x05\x03\xe4\x95\...
recvmsg(3, {msg_name(12)={sa_family=AF_NETLINK, pid=0, groups=00000000},
    msg_iov(1)=[{"\x78\x00\x00\x00\x07\x06\x02\x00\xe4\...

Here you can just copy packet strings from sendto and recvmsg, place in a file and use scripts/decoder.py to inspect them:

$ export PYTHONPATH=`pwd`
$ python scripts/decoder.py \
    pyroute2.netlink.nfnetlink.ipset.ipset_msg \
    scripts/ipset_01.data

See collected samples in the scripts directory. The script ignores spaces and allows multiple messages in the same file.

pyroute2-0.4.21/docs/html/general.html0000664000175000017500000005704213152333365017520 0ustar peetpeet00000000000000 pyroute2 — pyroute2 0.4.21 documentation

pyroute2¶

Pyroute2 is a pure Python netlink library. It requires only Python stdlib, no 3rd party libraries. The library was started as an RTNL protocol implementation, so the name is pyroute2, but now it supports many netlink protocols. Some supported netlink families and protocols:

  • rtnl, network settings — addresses, routes, traffic controls
  • nfnetlink — netfilter API: ipset, nftables, ...
  • ipq — simplest userspace packet filtering, iptables QUEUE target
  • devlink — manage and monitor devlink-enabled hardware
  • generic — generic netlink families
    • nl80211 — wireless functions API (basic support)
    • taskstats — extended process statistics
    • acpi_events — ACPI events monitoring
    • thermal_events — thermal events monitoring
    • VFS_DQUOT — disk quota events monitoring

the simplest usecase¶

The socket objects, provided by the library, are actual socket objects with a little bit extended API. The additional functionality aims to:

  • Help to open/bind netlink sockets
  • Discover generic netlink protocols and multicast groups
  • Construct, encode and decode netlink messages

Maybe the simplest usecase is to monitor events. Disk quota events:

from pyroute2 import DQuotSocket
# DQuotSocket automatically performs discovery and binding,
# since it has no other functionality beside of the monitoring
with DQuotSocket() as ds:
    for message in ds.get():
        print(message)

Or IPRoute:

from pyroute2 import IPRoute
with IPRoute() as ipr:
    # With IPRoute objects you have to call bind() manually
    ipr.bind()
    for message in ipr.get():
        print(message)

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.7

The pyroute2 testing framework requires flake8, coverage, nosetests.

pyroute2-0.4.21/docs/html/makefile.html0000664000175000017500000002413713152333365017657 0ustar peetpeet00000000000000 makefile documentation — pyroute2 0.4.21 documentation

makefile 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: epydoc¶

Build API documentation. Requires epydoc.

Pls notice that epydoc is discontinued. The reason to support it here is that it performs additional API testing and helps to highlight API inconsistencies.

No issues regarding epydoc output format are accepted.

target: test¶

Run tests against current code. Command line options:

  • python – path to the Python to use
  • nosetests – path to nosetests to use
  • wlevel – the Python -W level
  • coverage – set coverage=html to get coverage report
  • pdb – set pdb=true to launch pdb on errors
  • module – run only specific test module
  • skip – skip tests by pattern

To run the full test cycle on the project, using a specific python, making html coverage report:

$ sudo make test python=python3 coverage=html

To run a specific test module:

$ sudo make test module=general:test_ipdb.py:TestExplicit

The module parameter syntax:

## module=package[:test_file.py[:TestClass[.test_case]]]

$ sudo make test module=lnst
$ sudo make test module=general:test_ipr.py
$ sudo make test module=general:test_ipdb.py:TestExplicit

There are several test packages:

  • general – common functional tests
  • eventlet – Neutron compatibility tests
  • lnst – LNST compatibility tests

For each package a new Python instance is launched, keep that in mind since it affects the code coverage collection.

It is possible to skip tests by a pattern:

$ sudo make test skip=test_stress

target: test-ci¶

Run tests on isolated VMs defined by tests/ci/configs/*xml.

Requires qemu, kvm, libvirt and civm script: https://github.com/svinota/civm

Command line options:

  • civm – path to the civm script (if it is not in $PATH)

target: dist¶

Make Python distribution package. Command line options:

  • python – the Python to use

target: install¶

Build 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

target: develop¶

Build the package and deploy the egg-link with setuptools. No code will be deployed into the system directories, but instead the local package directory will be visible to the python. In that case one can change the code locally and immediately test it system-wide without running make install.

  • python – the Python to use

other targets¶

Other targets are either utility targets to be used internally, or hooks for related projects. You can safely ignore them.

pyroute2-0.4.21/docs/html/objects.inv0000664000175000017500000000220713152333366017356 0ustar peetpeet00000000000000# Sphinx inventory version 2 # Project: pyroute2 # Version: 0.4 # The remainder of this file is compressed using zlib. xÚµ˜Ksã(ÇïúTÍ^­š×ijkÙ9Lª6)ïøžŽ%Æ4€ìøÛ/IÅqôÚKb7ýû»›gCuQ²6ð9¥©PuùVJZs@‘3€…)ùoÚü6Iº7ŸjIŽ` é÷o¿îz±ÖèS 2Oó60…€35ªz”«h––˜‰(õÆúfê=“>n¿?„É^¹;@ª gڀÚÌy†É1 t¬¬aÙ|$ØØîåè·ؐ"N2`߀~v¿8zb/~0æ‘)ŠT zeŸ©J*³Dá@³E8¯u±_š†Wiô :v~,’±+zqJN£™ËC¡pÀ57+ôS;0nWóbÎÄq±ˆ–™Tk Õ+(œ­0}~SŠÉb•uF|•õpâX,qf)¿çRëj‰L3S–\§¹îP[À²„vãØîe~âóÄ£Rk48ùÏúÞQn)ònP˜ зw¢Æ•ÔX(ÀƒÍR[ßÌgµQò2nŒyš[Ôº®\ýt¯àÄ4“ƒ%>Z¬L‡‡Êh˜Ûúr©@àrf¿ë3®æ‘vKžï¹6ŒO^L¿cv\k{J¿qCêŒãñô +]`.ÄXežVöñuDkñ?EϋWÁí‰[ê<%\ïë/Ÿ§ÝLoèhRØ —òkiq‰YIŒ*µ’RqÐd-)“MVjϲ`óú (µ ƈnš\Ɉ;œú_M }kw?@ϹIþŒ‹6ÿæ)»‚KylÇ£iP'_8ŽÎԎÝü¹LO8ÂÓçÝVV Æg~¿ùu_çÚi£×ïO ¬HŽ¡ßš$hó ¹ïM+zòä Ì1µ‚„XäÀe"œÑs›„BVGÁ·¶{£7¡BžŽþ'"…Q,s•}Ä žß*ù˃z»Ë*¯'“ƒe«…X£µ†iiÄñÅvUr-„PXõ;þ•̕πy:™îÁ¹gUԶɍ¢xº!sŽLPxi<9΀·ŸÕ;?ºÉÕ­ <:¹®RCœe «Kâ;#Š{üDÍ£¬O7éÞ>#ÇëF‡ºëWë^â#؛\äßÙ<Ð{ØÖºa°±Äò7’­.›®!šƒ>í²âÑ ¹ï­ÛöŸ];—ºŽ¿°fíæ_â÷ËØ«]ÎǶ•Xà\ÈÈ®;ubpNúª6Ã#=žEÝ᝱ V­)DÍsiÀ{‹§ÛVŒìâH4ô‹³ï!oóÎ;ߟµQ&ºÜ0akŒ&ñLŽ<÷§È0ÃᯀÖ݃wû·fäš ¶sø?UÞpyroute2-0.4.21/docs/html/nlsocket.html0000664000175000017500000005124213152333365017721 0ustar peetpeet00000000000000 Base netlink socket and marshal — pyroute2 0.4.21 documentation
pyroute2-0.4.21/docs/html/devmodules.html0000664000175000017500000005651513152333365020256 0ustar peetpeet00000000000000 Modules in progress — pyroute2 0.4.21 documentation

Modules in progress¶

There are several modules in the very initial development state, and the help with them will be particularly valuable. You are more than just welcome to help with:

IPSet module¶

ipset support.

This module is tested with hash:ip, hash:net, list:set and several other ipset structures (like hash:net,iface). There is no guarantee that this module is working with all available ipset modules.

It supports almost all kernel commands (create, destroy, flush, rename, swap, test...)

class pyroute2.ipset.IPSet(version=6, attr_revision=None, nfgen_family=2)¶

NFNetlink socket (family=NETLINK_NETFILTER).

Implements API to the ipset functionality.

add(name, entry, family=2, exclusive=True, comment=None, timeout=None, etype='ip', **kwargs)¶

Add a member to the ipset.

etype is the entry type that you add to the ipset. It’s related to the ipset type. For example, use “ip” for one hash:ip or bitmap:ip ipset.

When your ipset store a tuple, like “hash:net,iface”, you must use a comma a separator (etype=”net,iface”)

create(name, stype='hash:ip', family=2, exclusive=True, counters=False, comment=False, maxelem=65536, forceadd=False, hashsize=None, timeout=None)¶

Create an ipset name of type stype, by default hash:ip.

Common ipset options are supported:

  • exclusive – if set, raise an error if the ipset exists
  • counters – enable data/packets counters
  • comment – enable comments capability
  • maxelem – max size of the ipset
  • forceadd – you should refer to the ipset manpage
  • hashsize – size of the hashtable (if any)
  • timeout – enable and set a default value for entries (if not None)
delete(name, entry, family=2, exclusive=True, etype='ip')¶

Delete a member from the ipset.

See add method for more information on etype.

destroy(name=None)¶

Destroy one or all ipset (when name is None)

flush(name=None)¶

Flush all ipsets. When name is set, flush only this ipset.

get_supported_revisions(stype, family=2)¶

Return minimum and maximum of revisions supported by the kernel

headers(name)¶

Get headers of the named ipset. It can be used to test if one ipset exists, since it returns a no such file or directory.

list(*argv, **kwarg)¶

List installed ipsets. If name is provided, list the named ipset or return an empty list.

Be warned: netlink does not return an error if given name does not exit, you will receive an empty list.

rename(name_src, name_dst)¶

Rename the ipset.

swap(set_a, set_b)¶

Swap two ipsets

test(name, entry, family=2, etype='ip')¶

Test if a member is part of an ipset

See add method for more information on etype.

IW module¶

Experimental wireless module — nl80211 support.

Disclaimer¶

Unlike IPRoute, which is mostly usable, though is far from complete yet, the IW module is in the very initial state. Neither the module itself, nor the message class cover the nl80211 functionality reasonably enough. So if you’re going to use it, brace yourself — debug is coming.

Messages¶

nl80211 messages are defined here:

pyroute2/netlink/nl80211/__init__.py

Pls notice NLAs of type hex. On the early development stage hex allows to inspect incoming data as a hex dump and, occasionally, even make requests with such NLAs. But it’s not a production way.

The type hex in the NLA definitions means that this particular NLA is not handled yet properly. If you want to use some NLA which is defined as hex yet, pls find out a specific type, patch the message class and submit your pull request on github.

If you’re not familiar with NLA types, take a look at RTNL definitions:

pyroute2/netlink/rtnl/ndmsg.py

and so on.

Communication with the kernel¶

There are several methods of the communication with the kernel.

  • sendto() — lowest possible, send a raw binary data
  • put() — send a netlink message
  • nlm_request() — send a message, return the response
  • get() — get a netlink message
  • recv() — get a raw binary data from the kernel

There are no errors on put() usually. Any permission denied, any invalid value errors are returned from the kernel with netlink also. So if you do put(), but don’t do get(), be prepared to miss errors.

The preferred method for the communication is nlm_request(). It tracks the message ID, returns the corresponding response. In the case of errors nlm_request() raises an exception. To get the response on any operation with nl80211, use flag NLM_F_ACK.

Reverse it¶

If you’re too lazy to read the kernel sources, but still need something not implemented here, you can use reverse engineering on a reference implementation. E.g.:

# strace -e trace=network -f -x -s 4096 \
        iw phy phy0 interface add test type monitor

Will dump all the netlink traffic between the program iw and the kernel. Three first packets are the generic netlink protocol discovery, you can ignore them. All that follows, is the nl80211 traffic:

sendmsg(3, {msg_name(12)={sa_family=AF_NETLINK, ... },
    msg_iov(1)=[{"\x30\x00\x00\x00\x1b\x00\x05 ...", 48}],
    msg_controllen=0, msg_flags=0}, 0) = 48
recvmsg(3, {msg_name(12)={sa_family=AF_NETLINK, ... },
    msg_iov(1)=[{"\x58\x00\x00\x00\x1b\x00\x00 ...", 16384}],
    msg_controllen=0, msg_flags=0}, 0) = 88
...

With -s 4096 you will get the full dump. Then copy the strings from msg_iov to a file, let’s say data, and run the decoder:

$ pwd
/home/user/Projects/pyroute2
$ export PYTHONPATH=`pwd`
$ python scripts/decoder.py pyroute2.netlink.nl80211.nl80211cmd data

You will get the session decoded:

{'attrs': [['NL80211_ATTR_WIPHY', 0],
           ['NL80211_ATTR_IFNAME', 'test'],
           ['NL80211_ATTR_IFTYPE', 6]],
 'cmd': 7,
 'header': {'flags': 5,
            'length': 48,
            'pid': 3292542647,
            'sequence_number': 1430426434,
            'type': 27},
 'reserved': 0,
 'version': 0}
{'attrs': [['NL80211_ATTR_IFINDEX', 23811],
           ['NL80211_ATTR_IFNAME', 'test'],
           ['NL80211_ATTR_WIPHY', 0],
           ['NL80211_ATTR_IFTYPE', 6],
           ['NL80211_ATTR_WDEV', 4],
           ['NL80211_ATTR_MAC', 'a4:4e:31:43:1c:7c'],
           ['NL80211_ATTR_GENERATION', '02:00:00:00']],
 'cmd': 7,
 'header': {'flags': 0,
            'length': 88,
            'pid': 3292542647,
            'sequence_number': 1430426434,
            'type': 27},
 'reserved': 0,
 'version': 1}

Now you know, how to do a request and what you will get as a response. Sample collected data is in the scripts directory.

Submit changes¶

Please do not hesitate to submit the changes on github. Without your patches this module will not evolve.

Network settings daemon – pyrouted¶

Pyrouted is a standalone project of a system service, that utilizes the pyroute2 library. It consists of a daemon controlled by systemd and a CLI utility that communicates with the daemon via UNIX socket.

It is an extremely simple and basic network interface setup tool.

pyroute2-0.4.21/docs/html/usage.html0000664000175000017500000003674713152333365017220 0ustar peetpeet00000000000000 Quickstart — pyroute2 0.4.21 documentation

Quickstart¶

Runtime¶

In the runtime pyroute2 socket objects behave as normal sockets. One can use them in the poll/select, one can call recv() and sendmsg():

from pyroute2 import IPRoute

# create RTNL socket
ipr = IPRoute()

# subscribe to broadcast messages
ipr.bind()

# wait for data (do not parse it)
data = ipr.recv(65535)

# parse received data
messages = ipr.marshal.parse(data)

# shortcut: recv() + parse()
#
# (under the hood is much more, but for
# simplicity it's enough to say so)
#
messages = ipr.get()

But pyroute2 objects have a lot of methods, written to handle specific tasks:

from pyroute2 import IPRoute
from pyroute2 import IW

# RTNL interface
ipr = IPRoute()

# WIFI interface
iw = IW()

# get devices list
ipr.get_links()

# scan WIFI networks on wlo1
iw.scan(ipr.link_lookup(ifname='wlo1'))

More info on specific modules is written in the next chapters.

Resource release¶

Do not forget to release resources and close sockets. Also keep in mind, that the real fd will be closed only when the Python GC will collect closed objects.

Signal handlers¶

If you place exclusive operations in a signal handler, the locking will not help. The only way to guard the handler is to ignore the signal from the handler:

import signal
from pyroute2 import IPDB

def handler(signum, frame):
    # emergency shutdown
    signal.signal(signal.SIGTERM, signal.SIG_IGN)
    ipdb.interfaces.test_if.remove().commit()
    ipdb.release()

def main():
    with IPDB() as ipdb:
        signal.signal(signal.SIGTERM, handler)
        test_if = ipdb.create(ifname='test_if', kind='dummy').commit()
        ...  # do some work

Imports¶

The public API is exported by pyroute2/__init__.py. There are two main reasons for such approach.

First, it is done so to provide a stable API, that will not be affected by changes in the package layout. There can be significant layout changes between versions, but if a symbol is re-exported via pyroute2/__init__.py, it will be available with the same import signature.

Warning

All other objects are also available for import, but they may change signatures in the next versions.

E.g.:

# Import a pyroute2 class directly. In the next versions
# the import signature can be changed, e.g., NetNS from
# pyroute2.netns.nslink it can be moved somewhere else.
#
from pyroute2.netns.nslink import NetNS
ns = NetNS('test')

# Import the same class from root module. This signature
# will stay the same, any layout change is reflected in
# the root module.
#
from pyroute2 import NetNS
ns = NetNS('test')

Another function of pyroute2/__init__.py is to provide deferred imports. Being imported from the root of the package, classes will be really imported only with the first constructor call. This make possible to change the base of pyroute2 classes on the fly.

The proxy class, used in the second case, supports correct isinstance() and issubclass() semantics, and in both cases the code will work in the same way.

There is an exception from the scheme: the exception classes.

Exceptions¶

Since the deferred import provides wrappers, not real classes, one can not use them in try: ... except: ... statements. So exception classes are simply reexported here.

Developers note: new exceptions modules must not import any other pyroute2 modules neither directly, nor indirectly. It means that __init__.py files in the import path should not contain pyroute2 symbols referred in the root module as that would cause import error due to recursion.

Special cases¶

eventlet¶

The eventlet environment conflicts in some way with socket objects, and pyroute2 provides a workaround for that:

# import symbols
#
import eventlet
from pyroute2 import NetNS
from pyroute2.config.eventlet import eventlet_config

# setup the environment
eventlet.monkey_patch()
eventlet_config()

# run the code
ns = NetNS('nsname')
ns.get_routes()
...
pyroute2-0.4.21/docs/html/devcontribute.html0000664000175000017500000001445513152333365020761 0ustar peetpeet00000000000000 Project contribution guide — pyroute2 0.4.21 documentation

Project contribution guide¶

To contribute the code to the project, you can use the github instruments: issues and pull-requests. See more on the project github page: https://github.com/svinota/pyroute2

Requirements¶

The code should comply with some requirements:

  • the library must work on Python >= 2.6 and 3.2.
  • the code must strictly comply with PEP8 (use flake8)
  • the ctypes usage must not break the library on SELinux

Testing¶

To perform code tests, run make test. Details about the makefile parameters see in README.make.md.

pyroute2-0.4.21/docs/html/searchindex.js0000664000175000017500000010273713152333366020053 0ustar peetpeet00000000000000Search.setIndex({docnames:["arch","changelog","debug","devcontribute","devgeneral","devmodules","dhcp","general","index","ipdb","iproute","makefile","mpls","netlink","netns","nlsocket","report","sm-interfaces","usage"],envversion:50,filenames:["arch.rst","changelog.rst","debug.rst","devcontribute.rst","devgeneral.rst","devmodules.rst","dhcp.rst","general.rst","index.rst","ipdb.rst","iproute.rst","makefile.rst","mpls.rst","netlink.rst","netns.rst","nlsocket.rst","report.rst","sm-interfaces.rst","usage.rst"],objects:{"pyroute2.dhcp":{dhcp4socket:[6,0,0,"-"]},"pyroute2.dhcp.dhcp4socket":{DHCP4Socket:[6,1,1,""]},"pyroute2.dhcp.dhcp4socket.DHCP4Socket":{get:[6,2,1,""],put:[6,2,1,""]},"pyroute2.ipdb":{main:[9,0,0,"-"]},"pyroute2.ipdb.main":{IPDB:[9,1,1,""]},"pyroute2.ipdb.main.IPDB":{register_callback:[9,2,1,""],release:[9,2,1,""]},"pyroute2.iproute":{IPBatch:[10,1,1,""],IPRoute:[10,1,1,""],IPRouteMixin:[10,1,1,""],RawIPRoute:[10,1,1,""]},"pyroute2.iproute.IPRouteMixin":{addr:[10,2,1,""],brport:[10,2,1,""],fdb:[10,2,1,""],flush_addr:[10,2,1,""],flush_routes:[10,2,1,""],flush_rules:[10,2,1,""],get_addr:[10,2,1,""],get_classes:[10,2,1,""],get_default_routes:[10,2,1,""],get_filters:[10,2,1,""],get_links:[10,2,1,""],get_neighbors:[10,2,1,""],get_neighbours:[10,2,1,""],get_ntables:[10,2,1,""],get_qdiscs:[10,2,1,""],get_routes:[10,2,1,""],get_rules:[10,2,1,""],get_vlans:[10,2,1,""],link:[10,2,1,""],link_lookup:[10,2,1,""],neigh:[10,2,1,""],route:[10,2,1,""],rule:[10,2,1,""],tc:[10,2,1,""],vlan_filter:[10,2,1,""]},"pyroute2.ipset":{IPSet:[5,1,1,""]},"pyroute2.ipset.IPSet":{"delete":[5,2,1,""],add:[5,2,1,""],create:[5,2,1,""],destroy:[5,2,1,""],flush:[5,2,1,""],get_supported_revisions:[5,2,1,""],headers:[5,2,1,""],list:[5,2,1,""],rename:[5,2,1,""],swap:[5,2,1,""],test:[5,2,1,""]},"pyroute2.netlink":{nlsocket:[15,0,0,"-"]},"pyroute2.netlink.nlsocket":{Marshal:[15,1,1,""],NetlinkMixin:[15,1,1,""]},"pyroute2.netlink.nlsocket.Marshal":{parse:[15,2,1,""]},"pyroute2.netlink.nlsocket.NetlinkMixin":{get:[15,2,1,""],get_policy_map:[15,2,1,""],put:[15,2,1,""],register_callback:[15,2,1,""],register_policy:[15,2,1,""],unregister_callback:[15,2,1,""],unregister_policy:[15,2,1,""]},"pyroute2.netlink.rtnl.tcmsg":{cls_u32:[10,0,0,"-"],sched_choke:[10,0,0,"-"],sched_clsact:[10,0,0,"-"],sched_drr:[10,0,0,"-"],sched_hfsc:[10,0,0,"-"],sched_htb:[10,0,0,"-"]},"pyroute2.netns":{create:[14,3,1,""],listnetns:[14,3,1,""],nslink:[14,0,0,"-"],remove:[14,3,1,""],setns:[14,3,1,""]},"pyroute2.netns.nslink":{NetNS:[14,1,1,""],NetNServer:[14,3,1,""]},"pyroute2.netns.nslink.NetNS":{remove:[14,2,1,""]},"pyroute2.netns.process":{proxy:[14,0,0,"-"]},"pyroute2.netns.process.proxy":{NSPopen:[14,1,1,""]},"pyroute2.netns.process.proxy.NSPopen":{release:[14,2,1,""]},pyroute2:{dhcp:[6,0,0,"-"],iproute:[10,0,0,"-"],ipset:[5,0,0,"-"],iwutil:[5,0,0,"-"],netlink:[13,0,0,"-"],netns:[14,0,0,"-"]}},objnames:{"0":["py","module","Python module"],"1":["py","class","Python class"],"2":["py","method","Python method"],"3":["py","function","Python function"]},objtypes:{"0":"py:module","1":"py:class","2":"py:method","3":"py:function"},terms:{"0ce40d31d937":9,"0x0":10,"0x0000":10,"0x0006":10,"0x00ff":10,"0x1":10,"0x10":10,"0x10000":10,"0x100000":10,"0x10001":10,"0x10010":10,"0x10020":10,"0x2":10,"0x20":10,"0x200":7,"0x200000":10,"0x300":7,"0x4":10,"0x5":10,"0x8":10,"0x8100":10,"0x88a8":10,"0x8b00":13,"0x8b01":13,"0x8b02":13,"0x8b03":13,"0xa":10,"0xc0a80000":10,"0xc0a80001":10,"0xc0a800010":10,"0xf":10,"0xff":10,"0xff0000":10,"0xffc0":10,"0xffff0000":10,"0xffff0001":10,"0xffffff00":10,"0xffffffff":10,"10kbit":10,"10mbit":10,"128kbit":10,"12x":13,"16byte":1,"16s":13,"192kbit":10,"1ad":[1,10],"256kbit":10,"2b7":17,"2nd":10,"32bit":1,"3rd":[7,10],"4c8":9,"4kl":17,"59e":17,"5mbit":10,"6\u04bfq":17,"7a637a44":9,"7fm":17,"\u0140":17,"\u01b4":17,"\u01be":17,"\u01d21":17,"\u0203v":17,"\u0213":17,"\u022f":17,"\u024bd\u04b5y":17,"\u0254n":17,"\u02635":17,"\u027d":17,"\u02aa":17,"\u0377":17,"\u03b0":17,"\u03cd":17,"\u03df":17,"\u0431":17,"\u043e":17,"\u046b":17,"\u047b":17,"\u04a5":17,"\u04b54":17,"\u04d5":17,"\u0581":17,"\u062di":17,"\u0630":17,"\u0643":17,"\u06a7b":17,"\u06ab":17,"\u06c6x":17,"\u06cb_i":17,"\u06d0":17,"\u06ef":17,"\u07246":17,"\u0756c":17,"\u07a1":17,"\u373fb":17,"\u8359":17,"\u8360":17,"\ub4d4":17,"\ud064y":17,"\ud147":17,"b8y\uac39":17,"boolean":13,"break":[1,3],"byte":[6,10,13,15],"case":[0,1,2,4,5,6,7,8,9,10,11,12,13,14,15,16],"catch":9,"class":[0,1,2,5,6,7,8,10,13,14,18],"default":[1,2,5,6,10,14,15],"export":[1,2,4,5,18],"float":10,"function":[1,4,5,6,7,9,10,11,14,15,18],"gn\u02cd":17,"import":[0,1,2,7,8,9,10,12,13,14,16],"int":[2,4,9,10,12,13,15],"j3\u06a0\u0584":17,"kn\u01fb3":17,"long":[9,10],"m\u0288y":17,"new":[1,9,10,11,12,13,14,18],"null":2,"nw\u043bm":17,"public":[0,1,10,18],"r\u4fb7pc":17,"return":[0,1,4,5,6,9,10,15],"short":[1,9,12],"static":10,"tg\ub312":17,"throw":1,"tn\u00f8izj":17,"true":[0,1,5,9,10,11,13,15],"try":[6,9,12,14,16,18],"veaj\u06e6":17,"vo\ub033":17,"while":[0,9,10,12],And:[10,13],Being:18,But:[0,4,5,6,9,13,18],For:[0,2,5,10,11,14],Hds:17,IPs:1,OVS:1,One:[4,6,9,10,12,13,14,15,18],Pls:[2,5,7,10,11,12],TLS:1,The:[0,2,3,4,5,6,7,8,10,11,12,13,14,15,18],Then:[5,15],There:[0,2,5,6,7,9,10,11,13,15,16,18],These:[0,9,10,14],Use:[2,10],Uses:10,Using:[9,13],VMs:[1,11],Will:[5,10],With:[5,7,10,15],__align:[10,13],__exit__:9,__init__:[4,5,13,18],__pad:13,__u32:13,__u8:13,_rproxi:0,_sproxi:0,a4c1:17,abil:10,abl:14,about:[3,8,9,13],abov:[2,6,9,10,13,14],absolut:1,ac_exitcod:13,ac_flag:13,accept:[0,6,9,10,11,12],access:[1,7,9,10,14],accord:[6,10],acess:9,achtung:1,acpi:7,acpi_ev:[1,7],act:[9,10],action:[1,9,10,15],activ:[9,15],actual:[4,7,9,10],add:[1,2,5,7,9,10,12,13,14],add_ip:[7,9,14],add_nh:9,add_port:[7,9],added:[1,6,9],addit:[7,9,10,11,14],addr:[1,2,7,9,10,12,13,15],address:[1,2,4,6,7,8,10,13,14],addrpool:1,administr:6,adt:1,advmss:9,af_bridg:10,af_inet6:[9,10],af_inet:[7,9,10,12,13],af_mpl:[1,7,12],af_netlink:[2,4,5],af_spec:10,affect:[0,10,11,13,18],after:[9,10,14,15],again:9,against:11,ageing:9,agnost:[0,10],aim:7,algo:4,algorithm:10,alia:10,alias:6,alignemt:13,all:[1,2,4,5,6,7,9,10,13,14,15,16,18],all_n:[10,15],alloc:1,allow:[0,1,4,5,7,9,10],almost:[5,9,10,15],alpha:1,alreadi:[0,6,10,14],also:[1,2,4,5,6,9,10,12,13,14,15,16,18],alwai:[2,10,13,15],analysi:6,android:[1,9],angu:1,ani:[0,1,2,4,5,6,9,10,12,14,15,18],anoth:[0,2,9,10,12,14,18],anymor:[2,9],anyth:9,anywai:2,apach:1,api:[0,1,4,5,7,8,11,14,15,18],appear:9,append:10,appli:[9,10],approach:[9,10,18],arch:[1,7],architectur:[1,8,14],arg:15,argument:[2,9,10,14,15],argv:[5,10,14],armv6l:1,armv7l:1,around:15,arp:[9,10],arrai:6,array8:6,arriv:[0,9,15],asap:0,ascii:13,asciiz:13,ask:9,assign:14,assum:[0,9,10,15],async:[0,4,8,9,10],asynchron:[0,8,9,10,13],asyncio:1,asyncron:9,attach:[2,10,13],attent:15,attr:[5,9,10,13],attr_revis:5,attribut:[1,6,9,10,15],auth:1,authomat:9,autocomplet:2,autodoc:1,autogener:13,autom:11,automat:[7,9,10,14,15],avail:[5,9,10,14,15,18],avoid:[9,15],avpkt:10,awar:14,b5e7:9,back:[1,7,9,10,14],background:[9,14,15],backlog:15,bad:14,bala:[9,10],bandwith:10,base:[1,4,8,9,10,18],bash:[2,10],basi:9,basic:[1,4,5,6,7,8,9,14],batch:[2,10],batchsocket:[0,2],bc4_:17,be16:[6,13],be32:[4,13],be64:13,be8:13,becaus:9,becom:[0,9,15],been:[9,10],befor:[7,9,10,14,15],begin:9,behav:[0,18],behavior:10,behaviour:[0,9,10,14],behind:0,being:[2,14],believ:10,below:[9,10,14],benchmark:1,besid:[0,7,13],best:14,better:[9,10],between:[1,5,9,14,15,18],bewar:15,big:[6,13],binari:[0,2,5,10,13],bind:[0,2,4,6,7,10,15,18],bit:[4,6,7,9,10,13],bitmap:5,bl2:17,blackhol:10,block:[1,7,9],bodi:10,bond:[1,9,10],bool:10,boot:10,bootp:6,bootrequest:6,bos:10,both:[1,9,10,13,18],bound:1,bpf:[1,6,10],br0:10,br0v500:10,br_:9,br_ageing_tim:9,br_forward_delai:9,brace:5,branch:10,brdch:14,brentri:10,bridg:[1,7,8,10],bring:[7,9,10],broadcast:[1,2,7,9,10,15,18],broken:1,brport:[1,10],brport_cost:9,brport_proxyarp:9,brport_unicast_flood:9,brx:10,bss:1,buf:0,buffer:[1,2,10,13,14,15],bufsiz:15,bug:[1,4,5,7,8],build:11,built:[4,11],bump:1,bundl:1,burst:[0,9,10,15],bytecod:11,c9y:17,cach:[9,10],cacheinfo:13,calcul:15,call:[0,1,2,7,9,10,14,15,18],callabl:[1,15],callback:[0,1,9,15],calm:15,can:[0,2,3,4,5,6,7,9,10,11,12,13,14,15,18],candi:9,capabl:[1,5],carefulli:10,carri:13,cascad:[1,13],cat:10,cau:17,caus:[0,2,9,10,18],cdata:13,cento:1,chaddr:6,chain:[1,10,13],chang:[1,4,7,9,10,11,13,18],changelog:8,channel:14,chantra:1,chapter:18,charact:10,cheaper:9,check:[4,9,14],child:14,choic:9,choke:1,choos:[0,1,10,13],civm:[1,11],classid:10,classifi:10,classless:10,clean:8,cleanup:1,clear:14,cli:5,client:[0,1],client_id:6,clone:2,close:[0,1,4,7,14,15,18],clsact:1,cmd:[5,14],cmdch:14,code:[0,1,3,4,6,9,10,11,12,13,14,16,18],codel:1,collect:[0,2,4,5,11,15,16,18],com:[3,5,7,9,11,16],combin:2,come:[0,5],comma:5,command:[1,2,5,9,10,11,13,14,16],comment:[2,5],commit:[0,1,7,9,10,12,14,18],common:[1,2,5,11,12],commun:[1,14],compar:9,comparison:1,compat:[0,1,6,8,11,14,15],compil:[1,8,10,11],complement:1,complet:[0,1,5,6,9,10,13],complex:15,compli:3,complic:[0,6,9],compon:1,con:9,condit:9,config:[1,11,16,18],configur:[7,10],conflict:[9,18],confus:10,connect:[0,4],connmark:1,consist:[1,4,5,9],constant:10,construct:[6,7,13,15],constructor:18,consum:15,consumpt:1,contain:[6,7,9,10,13,15,18],content:10,context:[1,6,8,14],continu:9,contribut:[8,10],control:[1,5,7,9,10,14],cooki:13,cope:[9,10],copi:[1,2,4,5],copyright:2,core:[4,14],correct:[1,18],correctli:[1,9],correspond:[0,5,6,9,13,14],cost:[9,10],count:10,counter:[1,5,15],cours:0,cover:[1,5,13],coverag:[1,7,11],cpu:[9,10,15],creat:[0,1,2,5,7,8,10,12,15,16,18],creation:[1,9,10],credit:2,critic:1,cstamp:13,ctrl:10,ctrlmsg:0,ctype:[3,14],current:[10,11,14,15],current_tx:9,curv:10,custom:[0,1,4,6,10,13],custom_socket_bas:4,cycl:[1,10,11],daemon:9,data:[0,1,4,5,8,10,11,13,14,15,18],databas:[4,9,10,12,14],date:9,deal:10,dealloc:1,debug:[1,5,8,13,15],declar:[1,13],decod:[0,1,4,5,6,7,8,15],dedic:[0,1,9,15],deepcopi:1,def:[4,10,15,18],defer:18,defin:[2,4,5,6,9,11,13],definit:[5,6,10],defragment:[1,15],del:[9,10],del_ip:9,del_nh:9,del_port:9,delai:[9,15],delet:[1,5,9,10,16],delta:[1,15],demand:[1,10],deni:5,depend:[1,2,9,10,14],deploi:[11,15],deploy:11,deprec:[1,10,15],deriv:15,describ:[4,6,7,13,14],descript:[6,10,13],descriptor:14,design:9,destin:[1,9,10],destroi:[5,10],destruct:1,detail:[3,6,7,9,10,13],dev:[1,2,7,10],develop:[1,4,5,6,9,13,18],devic:[1,7,9,10,15,18],devlink:[1,7],dhcp4msg:[0,6],dhcp4socket:6,dhcp:[0,1],dhcpdiscov:6,dhcpmsg:0,dhcprequest:6,dhcpv4:1,diagnost:1,diagram:[0,1,14],dict:[4,9,10,15],dictionari:[0,6,9,10,13,15],differ:[0,1,2,4,7,13,14,16],dir:9,direct:[1,10],direct_act:10,directli:[9,13,18],directori:[4,5,11],disabl:1,disciplin:8,discontinu:11,discov:7,discoveri:[5,7],disk:7,displai:2,dist:8,distinguish:13,distribut:11,doc:[1,4,7,8,9],document:[1,4,7,8,10],doe:[0,5,6,9,10,14],doesn:[0,1,2,8,9,10,13,14],don:[5,7,15],done:[0,6,9,14,18],dot:2,down:[1,9,10,14,15],dport:6,dquotsocket:7,draft:1,driver:10,drop:[1,9,10,14],drr:1,dst:[7,9,10,12],dst_len:10,dual:1,due:[2,18],dummi:[1,2,10,18],dummy0:10,dump:[1,4,5,8,10,13,14],duplex:14,dure:[1,9],each:[10,11,13],earli:5,easi:[4,9,13],easier:9,easiest:2,ecn:10,eexist:1,effect:9,egg:11,egress:10,eht0:10,either:[2,9,10,11,13,15],element:13,els:[9,10,18],elsewher:10,em1:[7,10],em2:7,emb:1,embed:1,emerg:18,empti:[1,5,10,13],enabl:[4,5,7],encap:[1,7,10,12],encod:[0,1,6,7,15],end:[2,6,7,9,13],endian:[6,13],enforc:14,engin:5,enobuf:8,enough:[4,5,9,15,18],enqueu:15,ensur:9,entiti:10,entri:5,environ:[16,18],epydoc:8,equal:[9,10],equival:10,ericsson:1,errno:[1,15],error:[0,1,5,7,9,10,11,13,15,18],establish:0,etc:[0,2,4,6,9,10,11,13,15,16],eth0:[9,10,12],eth1:[9,10,12],eth2:[9,10],eth:[9,10],eth_p_al:10,ether:13,ethmsg:0,etyp:5,even:[2,5,9,10,14,15],event:[1,7,9,10,13],eventlet:[1,4,8,11,16],eventlet_config:18,everi:[4,6,9,10,13],evolv:5,exactli:[0,14],exampl:[1,2,4,5,6,7,9,10,13,14,15],exce:15,except:[0,1,5,6,7,8,9,10,14,15],exclus:[5,9,18],execut:9,exist:[1,5,9,10,13,14],exit:[2,5,7,9,14],expect:[9,10,15],experi:9,experiment:5,explicit:[9,10,13],explicitli:[1,9,10,13,14],extend:[0,6,7,15],extern:[0,9,10],extra:0,extrem:5,eye:9,f_getfl:14,fact:4,fail:[1,9,10,14],fals:[5,9,10,15],famili:[1,5,7,9,10,12,13,15],familiar:5,far:[5,6,9,15],fast:15,faster:15,fbbpk:17,fcntl:14,fdb:[1,10],fe80:9,featur:[0,4,9,10],fedora:1,fetch:10,fff2:10,fff3:10,ffff:10,fibmsg:[0,10],field:[0,1,9,10,12,14],field_name1:13,field_namex:13,file:[1,2,4,5,10,11,14,18],fileno:[0,10,15],fill:13,filter:[1,6,7,8,9,15],find:[5,6,9,13],first:[1,5,6,9,10,14,15,18],fit:6,fix:[1,4,6,9,13,15],fix_messag:4,flag:[1,2,5,9,10,13,14,15],flake8:[3,7],flat:1,flock:14,flood:9,flush:[5,10],flush_addr:[1,10],flush_rout:[1,10],flush_rul:[1,10],fly:18,follow:[2,5,6,9,10,13],foo:10,foo_msg:13,forc:9,forceadd:5,forget:[4,7,9,14,18],fork:1,form:[2,9,10,13],format:[2,4,6,9,11,12,13,15],forum:7,forward:[0,1,9,10,14],found:4,four:[9,10],fq_codel:1,fr_act_nop:10,fr_act_unreach:10,frame:18,framework:[0,4,6,7],freez:[1,9],freq_fix:1,friendli:[2,4],from:[0,1,2,4,5,6,7,10,11,12,13,14,15,16,18],fsc:10,full:[1,5,9,10,11,14],fun:10,further:[6,14],futur:9,fwmark:[1,10],gact:10,gatewai:[7,9,10,12],gcc:2,gener:[0,1,4,5,6,7,11,12,13,15],genericnetlinksocket:0,genl:1,genlmsg:[0,4],get:[0,1,5,6,7,10,11,12,13,14,15,18],get_addr:10,get_attr:10,get_class:10,get_default_rout:10,get_filt:10,get_link:[0,7,10,18],get_neighbor:10,get_neighbour:10,get_ntabl:10,get_policy_map:15,get_qdisc:10,get_rout:[10,18],get_rul:10,get_supported_revis:5,get_vlan:[1,10],getenforc:14,getpid:[13,15],getsocknam:[2,4],getsockopt:15,getvalu:0,gevent:16,gid:10,git:2,github:[3,4,5,7,9,11,12,16],give:9,given:[5,6,15],gix:17,global:1,going:[0,5],googl:7,gplv2:1,gre:10,gre_iflag:10,gre_ikei:10,gre_loc:10,gre_oflag:10,gre_okei:10,gre_remot:10,gre_ttl:10,gretap:1,grex:10,group:[1,2,4,6,7],gtp:1,guarante:[0,5,13],guard:18,gui:1,guid:[1,8],gvf:17,h6S:17,had:10,half:1,handl:[0,1,2,5,9,10,14,18],handler:8,happen:9,happi:4,hardwar:7,has:[4,6,7,9,10,14],hash:[1,5],hashsiz:5,hashtabl:5,hat:2,have:[0,1,2,6,7,9,10,13,14,16,18],header:[1,2,5,6,10,13,15],help:[2,4,5,7,8,10,11,18],here:[4,5,6,7,9,10,11,13,14,18],hesit:5,hex:[2,4,5,13],hexdump:2,hfsc:1,hide:0,hierarchi:[0,1,4,10],high:[0,4,7,10,13],highlight:11,hint:6,histor:9,home:[5,7],hood:18,hook:[9,11],hop:[9,10],hoplimit:[7,9,10],host:[2,9],how:[0,2,4,5],howev:10,htb:1,html:11,http:[3,5,7,9,11,16],hundr:[9,10],i386:1,i686:1,idea:0,identifi:[9,10],idpb:12,ids:1,idx:[7,9,10,12,14],if_slav:1,ifa_address:13,ifa_cacheinfo:13,ifa_famili:13,ifa_flag:13,ifa_index:13,ifa_label:10,ifa_loc:13,ifa_pref:13,ifa_prefixlen:13,ifa_scop:13,ifa_unspec:13,ifa_valid:13,ifac:[5,9],ifaddrmsg:[0,10,13,15],ifalia:1,ifconfig:10,ifdb:9,ifi_chang:2,ifi_famili:2,ifi_flag:2,ifi_index:2,ifi_typ:[2,10,13],ifindex:10,ifinfbas:0,ifinfmsg:[0,1,2,10,15],ifinfveth:0,ifla_af_spec:10,ifla_carri:10,ifla_group:10,ifla_ifnam:[10,13],ifla_info_data:[1,10],ifla_info_kind:10,ifla_linkinfo:10,ifla_linkmod:10,ifla_macvlan_macaddr:10,ifla_mtu:10,ifla_net_ns_fd:14,ifla_num_rx_queu:10,ifla_num_tx_queu:10,ifla_operst:10,ifla_promiscu:10,ifla_txqlen:10,ifla_wireless:13,iflag:10,ifnam:[1,2,6,7,9,10,12,14,18],ifr:10,ignor:[2,4,5,11,13,15,18],ignore_rt:9,iif:[9,10],iifnam:10,immedi:[9,11,15],implement:[0,1,4,5,6,7,9,10,14,15],impli:9,implicit:[7,9,10],implicitli:14,improv:1,imq:10,incapsul:13,includ:[1,10,13,15],incom:[0,5,6,10,14],incomplet:2,inconsist:11,incorrect:1,increas:[1,15],indec:10,index:[1,2,7,8,9,10,12,13,14,15],indic:10,indirectli:18,info:[1,9,10,18],inform:[2,5,9,13,16],ingress:[1,10],inherit:[0,1,6,13],init:[0,1],initi:[1,5,6,13],inject:[10,12],inor:1,input:[9,10],insert:9,insid:2,inspect:[2,4,5,6],instal:[1,5,6,8,10],instanc:[6,9,11,14,15],instanti:[10,13],instead:[0,1,2,4,9,10,11,13,14],instruct:2,instrument:3,int32:6,intefac:9,integ:[9,10],integr:[1,10],intend:[2,4,10,14],intens:15,intention:6,interact:9,interest:[0,6],interfac:[0,1,5,6,7,8,10,12,13,16,18],interfer:9,intern:[1,9,10,11],internet:8,interpret:[6,14],intersect:1,intf:9,introduc:10,introduct:12,invalid:5,invok:[9,15],iobrok:1,iocor:1,ioctl:[0,10,14],ioloop:1,ip4addr:[4,13],ip4msg:0,ip6addr:13,ip6gr:10,ip6gre_:10,ipaddr:[1,7,9,13],ipb:[2,10],ipbatch:[0,2,10],ipbatchsocket:0,ipc:1,ipdb:[1,4,7,8,13,14,18],ipdb_main:14,ipdb_test:14,ipq:[0,1,7],ipq_base_msg:0,ipq_mode_msg:[0,13],ipq_packet_msg:0,ipq_verdict_msg:0,ipqsocket:0,ipr:[0,2,4,7,10,12,14,15,18],iprout:[0,1,2,4,5,7,8,13,14,15,18],iproute2:[10,13],iproutemixin:[0,2,10],iprouterequest:10,iprsocket:[0,1,4,15],iprsocketmixin:0,ipset:[0,1,4,7],ipset_01:4,ipset_attr_adt:1,ipset_attr_data:1,ipset_cmd_list:4,ipset_cmd_protocol:4,ipset_msg:[0,4],iptabl:7,ipv4:[1,9,10,13],ipv6:[1,6,9,10,13],ipvlan:1,isinst:[1,18],isol:[1,11],issu:[1,3,4,5,7,8,11,12,14,16],issubclass:[1,18],issuecom:9,item:6,iter:[9,13,15],its:[2,4,6,10,13,14],itself:[0,4,5,7,9,10,13],iw_ev:13,iwutil:[0,1,4],j0d:17,join:9,just:[0,2,4,5,6,9,10,13,14,15],k6p:17,kdm:17,keep:[9,10,11,13,14,15,16,18],kei:[0,1,4,6,9,10,12,15],kernel:[0,1,2,4,9,10,12,13,16],keyword:[9,10],kill_rtcach:9,kind:[2,7,9,10,14,18],knife:10,know:[5,10],kvm:11,kwarg:[5,9,10,14],l2addr:[4,6,13],label:[7,8,10],lambda:[6,10,15],last:15,later:[0,2,6,9,10],latest:3,latter:9,launch:[10,11,15],layer:0,layout:[1,13,18],lazi:5,leak:1,least:9,leav:[9,10],lee:1,legal:1,len:[9,10,13],length:[2,5,10,13],less:[4,6,10],let:[0,1,4,5,10],letter:10,level:[0,4,7,9,10,11,13,14,15],lib:11,libc6:2,libc:14,librari:[0,2,3,4,5,6,7,9,10,14,15],libvirt:11,licens:[1,2],like:[4,5,6,9,10,13,14],limit:[0,10],line:[2,11],link:[1,2,8,9,10,11,14],link_creat:7,link_lookup:[7,10,12,14,18],linkedset:1,linux:[2,7,9,10,12,16],list:[0,1,4,5,7,8,9,10,12,13,15,18],listen:1,listnetn:[7,14],liter:6,littl:7,lladdr:10,lnst:11,load:[1,4,9,10,14,15],local:[1,2,9,10,11],locat:14,lock:[9,18],lockf:14,log:15,logic:0,logo:1,longer:9,look:[5,6,13],lookup:[7,9,10,14],loop:[1,9,14,15],loopback:[7,15],lot:18,low:[4,7,14],lower:10,lowest:5,lqt:17,lucki:13,lwtunnel:[1,7,10],mac:[1,6,10,13],macaddr:9,machin:6,macvlan:[1,10],macvlan_mod:10,macvtap:[1,10],macvtap_mod:10,made:9,magic:0,mai:[1,9,10,13,18],main:[0,1,7,9,14,18],maintain:9,make:[1,2,3,5,7,9,10,11,13,15,16,18],makefil:[3,8],man:13,manag:[0,1,6,7,8,10],mandatori:14,mani:[1,6,7,9,10,12,15],manipul:[4,7],manpag:5,manual:[7,13],map:[1,7,13],mar:2,mark:[1,13],marshal:[2,8,18],mask:[1,2,9,10,13],masquerad:1,massiv:15,master:[1,10],match:[0,1,4,9,10],matter:13,max:[5,10],maxelem:5,maximum:5,mayb:7,mean:[0,5,6,9,10,13,15,18],meaningless:14,measur:16,mechan:10,member:[5,13],memori:1,mention:[2,14],merg:[6,9],messag:[1,2,6,7,8,9,15,18],message_typ:6,method:[0,1,2,4,5,6,9,10,14,18],metric:[1,7,10],min:10,mind:[9,10,11,13,14,15,16,18],minimum:5,minor:1,mip:1,mir:1,mirror:1,mismatch:9,miss:[5,10],mitig:1,mix:13,mixin:[0,10],mode:[1,8,10],model:1,modern:[9,10,14],modifi:[9,10],modprob:7,modul:[1,2,6,7,8,11,12,13,14,18],moment:[9,15],monitor:[1,5,7,9],monkey_patch:18,more:[1,2,3,4,5,6,7,9,10,13,14,15,18],moreov:9,most:9,mostli:[5,6,11,15],move:[1,6,7,8,18],movement:1,mpl:[1,7,8,10,13],mpls_iptunnel:[7,12],mpls_router:[7,12],msg:[0,6,9,10,13,15],msg_class:15,msg_controllen:[2,5],msg_flag:[0,2,5,15],msg_id1:15,msg_id:15,msg_iov:[2,4,5],msg_name:[2,4,5],msg_pid:15,msg_seq:15,msg_type:15,mtu:[7,9,10],much:[9,14,15,18],multi:1,multicast:[7,9],multipath:[1,10,12],multipl:[1,4,9],multiprocess:14,must:[0,3,5,6,7,10,13,15,18],mutabl:13,mvlan0:10,mvq0:17,mvtap0:10,my_program:14,myprog:10,n2u:17,name:[1,5,6,7,9,10,13,14],name_dst:5,name_src:5,namespac:[0,1,8,10],nativ:15,natur:0,ndb:10,ndmsg:[0,5,10],need:[5,6,9,10,13,15,16],neigh:10,neighbour:[1,10],neither:[5,9,18],nest:13,net:[1,5,7,10,12],net_ns_:1,net_ns_fd:[1,7,14],net_ns_pid:1,netem:1,netfilt:7,netlink:[1,4,5,6,7,9,10,14],netlink_listen_all_nsid:1,netlink_netfilt:[4,5],netlink_rout:2,netlink_sock_diag:1,netlinkerror:1,netlinkmixin:[0,15],netlinksocket:[0,1,10,15],netn:[0,1,4,7,8,10,18],netns_nam:14,netnserv:14,network:[0,1,2,4,6,8,9,10,14,15,18],networkmanag:9,neutron:11,never:13,newdst:[7,12],newli:9,next:[0,1,6,18],nexthop:9,nfgen_famili:5,nfgen_msg:0,nfnetlink:[0,1,2,4,5,7],nftabl:[1,7],nic:10,nl80211:[0,1,2,4,5,7],nl80211_attr_gener:5,nl80211_attr_ifindex:5,nl80211_attr_ifnam:5,nl80211_attr_iftyp:5,nl80211_attr_mac:5,nl80211_attr_wdev:5,nl80211_attr_wiphi:5,nl80211cmd:[0,5],nl_async:9,nl_bind_group:9,nla:[0,1,4,5,10,13,14],nla_bas:0,nla_f_nest:13,nla_map:10,nla_nam:13,nla_name1:13,nla_namei:13,nla_namex:13,nla_typ:13,nlflag:0,nlm_f_ack:[5,10,13],nlm_f_creat:[10,13],nlm_f_excl:[10,13],nlm_f_request:[10,13],nlm_request:[0,5,10],nlmsg:[0,1,2,4,10,13],nlmsg_atom:0,nlmsg_base:0,nlsock:13,nlsocket:[0,1,15],nmcli:9,node:10,non:[1,9],nonc:[1,13],none:[5,6,9,10,13,14,15],nope:14,nor:[5,9,18],normal:[1,9,10,14,15,18],nose:3,nosetest:[3,7,11],notat:[1,9,10,12,13],note:[8,9,10,13,15,18],noth:[6,14],notic:[2,4,5,7,9,10,11,12,13,14,15],now:[1,2,5,6,7,10],nslink:[0,14,18],nsname:[14,18],nsp:14,nspath:14,nspopen:[1,8],nsy:17,number:[2,6,12,13,14,15],nvie:3,o_creat:14,o_excl:14,o_exist:14,obj:15,object:[0,1,4,7,9,10,13,14,15,18],obsolet:1,occasion:[5,9],occur:7,offset:[10,14],oflag:10,often:[9,10],oif:[7,9,10,12],oifnam:10,old:[1,9,10],older:2,onc:[2,9,15],one:[0,1,2,4,5,6,9,10,11,12,13,14,15,18],ones:[10,15],onli:[0,1,2,4,5,6,7,8,10,11,12,13,14,15,18],oop:1,open:[7,14],open_bpf_fd:10,oper:[1,5,9,10,14,18],operst:10,optim:1,option:[1,5,9,10,11,13,15],order:[0,13,14],ordinari:[0,6],org:[3,5,7],other:[0,1,4,5,6,7,8,10,13,14,15,18],otherwis:[4,10],out:[5,6,9],outgo:[6,10],output:[1,2,4,9,10,11],outsid:[9,10],over:[0,1,10,15],overflow:[10,15],overview:8,ovs:[0,1],own:[1,2,4,9,10,13,14],p2p:10,p6p1:10,pack:[6,13],packag:[1,2,11,18],packet:[0,1,2,4,5,7,9,10,12,14,15],pad:[6,13],page:[3,4,8,12,13],pair:[0,7,9,10,14],paramet:[1,3,6,9,10,11,14,15,16],parameter_list:6,parent:[10,13],pars:[0,1,2,4,10,14,15,18],parser:[1,2,6,10,15],part:[4,5,10],parti:7,partial:1,particular:[1,5,10,15],particularli:5,partli:4,pass:[0,1,2,9,10,13],passthru:10,patch:[5,9],path:[1,9,11,18],pattern:11,payload:13,pdb:11,peer:[1,7,10,14],pep8:3,perform:[3,6,7,8,10,11,15],perman:10,permiss:5,permit:13,perspect:10,perturb:10,pf_local:2,pf_netlink:[2,4],phy0:5,phy:5,pickl:[1,14],pid:[2,4,5,10,13,14,15],pip:[2,7,10],pipe:[1,10,14],place:[4,9,18],plai:[0,15],plan:[9,10,15],platform:[1,14,16],platform_label:[7,12],pleas:[4,5,9,10,13,14,15,16],pls:[5,9],plug:1,plugin:[1,9,10],point:[9,10],polic:[1,10],polici:[1,4,6,9,10,15],poll:[0,6,9,14,15,18],popen:14,port0:9,port:[1,4,6,7,8,10,15],possibl:[0,2,4,5,6,7,9,10,11,12,13,14,15,16,18],post:9,ppc64:1,pprint:[7,16],pre:[9,13],predic:[10,15],prefer:[5,10],preferr:0,prefix:[9,10],prefixlen:[7,9,10,13],prefsrc:10,prepar:[0,1,5],present:10,pretti:0,prevent:9,previou:10,primari:[1,10,13],primarili:4,print:[7,9,10,12,14,15],prio:10,priomap:1,prior:[9,10,15],priorit:15,prioriti:[1,10,15],privat:10,probabl:[2,10],proc:[1,14],procedur:6,process:[1,2,7,8,9,10,11,13,15],produc:2,product:[5,15],profil:10,program:[5,9,10,15],programm:10,progress:[1,7],project:[1,2,4,5,7,8,9,11,12,16],promote_secondari:13,proper:[0,13],properli:[5,9,10],properti:9,protinfo_bridg:10,proto:[2,10],protocol:[1,2,5,7,8,10,13,14],provid:[0,1,2,4,5,6,7,9,10,13,14,15,16,18],proxi:[0,1,4,10,14,18],proxyarp:9,psched:1,ptrace:1,pull:[1,3,5],pure:7,purg:10,purpos:14,push:[1,7,12],put:[0,5,6,10,15],pvid:10,pwd:[2,4,5],pyinstal:1,pypi:[1,3,5,7],pyrout:2,pyroute2:[0,2,3,4,5,6,9,10,11,12,14,15,16,18],pyroute2_tests_ro:1,python2:[1,2],python3:[1,2,11],python:[1,2,3,4,5,7,9,10,11,13,14,15,16,18],pythonpath:[2,4,5],qdisc:[1,10],qemu:11,qpl:17,qpython:1,qsize:15,quantum:10,queri:0,queu:15,queue:[0,1,7,8,15],quick:14,quickstart:8,quit:12,quota:7,race:[1,9],rais:[0,5,9,10],rang:13,range_begin:10,range_end:10,rate:10,rational:4,raw:[2,5,6,10,15],rawiprout:[0,10],rawiprsocket:0,rawiprsocketmixin:0,rcvch:14,react:9,read:[1,5,7,8,10,14,15],reader:[9,15],readi:9,readm:3,readthedoc:3,real:[10,18],realli:[9,13,18],realm:10,reason:[5,9,10,11,13,14,18],reassembl:1,receiv:[5,10,15,18],recogn:9,record:[1,9,10],recreat:9,recurs:[4,10,13,18],recv:[0,2,5,14,15,18],recvmsg:[2,4,5],red:2,redirect:10,reexport:18,refactor:1,refer:[1,4,5,9,10,12,13,14,15,18],reflect:[9,10,12,18],regard:11,regist:[0,15],register_callback:[9,15],register_polici:[4,15],regular:9,regularli:10,reject:10,rel:1,relat:[1,5,11,14],releas:[1,7,8,9,14],relev:10,reli:9,remot:[0,1],remotesocket:0,remov:[1,7,8,9,10,13,15,18],renam:[5,9,10],rep:1,replac:[1,10,14,15],repo:[2,11],report:[8,10,11],repres:[1,9,10,13],req:[1,10,12],request:[0,1,2,3,4,5,9,10,12,13,14,15],requested_ip:6,requir:[0,1,2,4,8,9,10,11,12,13,14,15,16],reserv:[5,10,13,15],reset:[0,2,10],resid:[6,12],resolv:13,resourc:[1,8,9,14,15],respect:1,respons:[0,1,2,5,8,13,14,15],rest:[2,6],restart:[1,10],restart_on_error:9,restor:9,restrict:9,result:[0,10,15],retriev:[9,10],reus:1,revert:9,review:[1,9,14],revis:[1,5],rework:1,rhel6:1,rhel7:1,rhel:1,rhev:7,right:6,riprsocket:0,risk:9,roll:[7,9],root:[1,10,11,15,18],rout:[0,1,2,7,8,10,14],routekei:9,routin:[1,6,9,10,15],rpc:1,rpm:[1,2],rsc:10,rt_proto:10,rt_scope:[10,13],rt_scope_host:10,rt_scope_link:10,rt_scope_nowher:10,rt_scope_sit:10,rt_scope_univers:10,rt_type:10,rta_flow:1,rta_via:1,rtab:1,rtax_hoplimit:[9,10],rtax_mtu:10,rtm_deladdr:15,rtm_dellink:15,rtm_delqdisc:10,rtm_getlink:[0,15],rtm_newaddr:[13,15],rtm_newlink:[9,10,13,15],rtm_newqdisc:10,rtmsg:[0,1,2,9,10],rtmsg_base:0,rtnetlink:[8,10],rtnl:[0,1,2,4,5,7,10,13,14,18],rtscope:10,rtt:9,rule:[1,2,6,8,10,15],rulekei:9,run:[0,2,3,4,5,9,10,11,14,15,16,18],runtim:[4,8,13],runtimeerror:9,rwva:17,sa_famili:[2,4,5],safe:[10,11,15],sai:[4,5,18],same:[0,1,2,4,9,10,13,14,15,18],sampl:[4,5,8,9,12,14],save:[10,15],scan:[13,18],scheme:[9,10,15,18],school:10,scope:[1,2,9,10,13],script:[2,4,5,9,11],search:8,second:[10,15,18],secondari:10,section:6,see:[0,1,3,4,5,7,9,10,13],seen:6,select:[6,14,15,18],self:[0,4,10,15],selinux:[3,14],semant:[14,18],send:[0,2,5,10,14,15],sendmsg:[5,18],sendto:[0,2,4,5,10,13,14,15],sens:0,sent:[2,6,10],separ:[1,5,9,13,14],seq:[13,15],sequenc:[1,2,9,13,14,15],sequence_numb:[0,2,5,10,13,15],serv:0,server:[1,6,14],server_id:6,servic:[1,5,10],session:[5,9],set:[0,1,2,4,7,8,9,10,11,12,13,15],set_a:5,set_address:9,set_b:5,set_mtu:9,set_nam:9,setn:[1,14],setsockopt:[2,15],setup:[1,5,9,10,18],setuptool:11,sever:[2,4,5,7,9,10,11,13,15],sfq:[1,10],shadow:1,share:[1,10],shortcut:[0,10,18],should:[0,2,3,4,5,6,9,10,12,13,14,15,18],shouldn:9,show:10,shown:[2,10],shut:[9,14],shutdown:[1,9,18],side:14,sig_ign:18,signal:8,signatur:[1,10,18],signific:[4,9,18],significantli:10,signum:18,sigterm:18,similar:6,simpl:[0,1,4,5,9,10,13,14],simpler:[9,13],simplest:[4,8,9,15],simplex:14,simpli:18,simplic:18,simplifi:13,sinc:[2,5,7,9,10,11,13,18],singl:[1,12],siocgiwnam:13,siocgiwnwid:13,siocgiwscan:13,siocsiwcommit:13,siocsiwnwid:13,situat:9,size:[5,10],sjr:17,skip:[2,11,12,13],slowli:6,so_rcvbuf:[2,15],so_sndbuf:[1,2],sock:6,sock_cloexec:2,sock_dgram:2,sock_diag:1,sock_raw:[2,4],socket:[1,2,4,5,7,8,10,12,13,14,18],sockopt:15,softwar:8,sol_socket:2,solut:[10,14,15],some:[0,1,2,3,4,5,6,7,9,10,12,13,14,15,18],some_attr:15,somenetn:14,someon:[7,9,10],someth:[4,5,15],sometim:[0,9,15,16],somewher:[4,18],soon:9,sor:17,sort:1,sort_address:9,sourc:[1,5,10],space:[2,4,13,15],spare:15,spawn:[8,10],speak:14,spec:[1,9,10],special:[8,9,10],specif:[1,5,6,9,10,11,13,14,16,18],specifi:[2,6,9,10,13,14,15],speed:1,sphinx:[1,11],src:[2,9,10],src_len:10,ssl:1,stabl:18,stack:[9,10,12],stage:5,stai:[9,18],standalon:5,standard:[10,14],start:[0,1,2,4,7,9,10,14,15],startswith:9,startup:[9,14],state:[1,2,5,6,7,9,10,14,15],statement:[6,7,9,10,18],statist:7,statu:14,stderr:[1,14],stdin:[1,14],stdlib:7,stdout:[1,14],step:13,still:[1,2,5,14,15],stop:[1,9,14],store:[5,10,15],storm:[9,15],stp:9,str:[2,10],strace:[4,5,8],stream:13,stress:15,strict:1,strictli:[3,14],string:[2,4,5,6,9,10,12,13,14,15],strip:6,struct:[6,10,13],structur:[1,4,5,6,10,13],stuff:[0,1,2,14],stype:5,sub:1,submit:12,subnet:10,subpackag:1,subprocess:14,subscrib:18,substitut:14,subsystem:[10,13],sudo:[4,7,9,10,11,12,16],supplementari:0,support:[0,1,4,5,7,9,10,11,12,14,15,18],suppos:[9,14],suppress:[9,15],sure:[2,6,9],surpris:4,svinota:[3,5,7,9,11,16],swap:[5,12],swiss:10,switchdev:10,symbol:[2,18],sync:[1,7,9],synchron:[9,14],syntax:[1,6,10,11],syscal:1,sysctl:[7,12,13],system:[0,5,6,7,9,10,11,14,16],systemd:5,tabl:[1,9,10,12],tag:10,take:[1,5,9,10,13,14],tap0:[9,10],tap:[1,10],tarbal:1,target:[1,7,8,10,13,14],task:[14,18],taskstat:[0,4,7],taskstatsmsg:0,tbf:1,tca_act_bpf:1,tca_u32_act:1,tcmd:0,tcmsg:[0,1],tcp:1,tcy:17,teamd:0,term:[9,10],termin:[13,15],test:[1,2,5,7,8,10,12,14,15,16,18],test_cas:11,test_fil:11,test_if:18,test_ipdb:[11,12],test_ipr:[11,12],test_netn:14,test_platform:16,test_stress:11,testcapsrtnl:16,testclass:11,testexplicit:11,than:[5,9,15],thank:1,thei:[0,2,4,6,9,10,12,13,15,18],them:[0,2,4,5,9,10,11,13,14,16,18],thereaft:[2,9],thermal:7,thermal_ev:[1,7],thi:[0,1,4,5,6,9,10,13,14,15,16,18],thing:9,think:[4,8],those:10,though:[5,9,10,13,14],thousand:[9,10],thread:[1,8,9,15],threadless:[1,8],three:[5,9,10,13],through:[0,10],thu:[4,7,9,10,14],time:[0,1,2,9,10,15],timeout:[1,5],timeoutexcept:1,tnum:9,tobyt:6,todo:10,togeth:10,tolist:6,too:[0,5,9,14],tool:[5,14],top:14,tos:[1,10],total:6,touch:9,trace:[2,4,5],track:5,tracker:16,tradit:10,traffic:[5,7,9,10],transact:[0,1,6,7,8],transfer:14,transform:6,transit:1,translat:10,transpar:[0,4,7,10,14],transport:[0,1,4,7,15],treat:[13,14],tree:13,tri:[9,10],tstamp:13,tstat:13,ttl:10,tu4:17,tun:[1,10],tunnel:10,tuntap:[0,1,10],tuntap_data:10,tupl:[5,9,13,14,15],turn:[9,14,15],tutori:9,two:[0,1,2,5,6,9,10,13,14,15,18],txqlen:[9,10],txt:10,type:[0,1,2,4,5,6,7,9,10,12,15,16],typic:14,typl:13,u32:1,udp4_pseudo_head:0,udp:[1,6],udpmsg:0,uid:10,uint16:[10,13],uint32:[4,13,14],uint64:[6,13],uint8:[6,13],uint:13,unchang:10,undefin:9,under:[1,2,14,18],underli:10,understand:9,unfreez:9,unicast:[9,10],unicast_flood:10,unicod:1,unif:10,uniform:9,uniqu:[10,13],unit:1,univer:13,unix:[1,5],unknown:10,unless:[9,10],unlik:[5,9,10],unmodifi:14,unnecessari:9,unpack:13,unreach:[9,10],unregist:15,unregister_callback:15,unregister_polici:15,unsign:13,untag:10,until:[2,7,9,13],updat:[1,9,10],uplink:1,upon:[9,10,15],upper:10,upstream:10,url:2,usabl:5,usag:[1,3,10,12],usc:10,use:[0,1,2,3,4,5,7,9,10,11,13,14,15,18],usecas:8,used:[1,2,4,5,6,10,11,13,14,15,16,18],useful:[0,4,9,10,13],user:[0,1,4,5,6,9],userspac:[0,7,15],uses:[0,1,4,9,10,14],using:[0,1,4,6,9,10,11,15],usual:[4,5,13],usus:10,utf:13,util:[0,1,5,7,9,11],uuid4:1,uuid:9,v0p0:[7,14],v0p1:[7,14],v100:10,v100c:10,v1m:17,v1p0:10,v1p1:10,v500:10,valid:0,valu:[4,5,6,10,13,15],valuabl:5,variabl:10,variant:[2,9],vector:10,vepa:10,verdict:0,veri:[5,6,13],version:[1,2,5,13,16,18],veth0:[9,10],veth1:9,veth2:9,veth:[1,7,10,14],vfs_dquot:[1,7],via:[0,1,2,5,9,10,12,14,18],vid:10,view:[1,8],vim:3,virbr0:[2,9,10],virtual:[9,10],visibl:[10,11],vlan:[1,10],vlan_filt:[1,10],vlan_flag:1,vlan_id:10,vlan_info:10,vlan_protocol:10,vni:10,vpn:9,vrf:[1,9,10],vrf_tabl:[1,10],vru:17,vx101:10,vx500:10,vxlan:[1,10],vxlan_data:10,vxlan_group:10,vxlan_id:10,vxlan_link:10,vxlan_ttl:10,wai:[0,4,5,6,9,10,13,14,15,18],wait:[1,7,9,14,18],wait_interfac:1,wait_ip:[1,7],want:[2,4,5,9,13,15],warn:[5,15],wast:10,watchdog:1,weight:9,weird:9,welcom:5,well:[4,9,13,14],were:[1,10,13],what:[2,5,9,10],when:[0,1,2,5,6,8,9,10,13,14,18],whenev:0,where:[0,9,10,11,12,13,14],whether:9,which:[5,9,10,14],whole:2,why:[2,9,14],wide:11,width:6,wifi:18,window:9,wireless:[4,5,7,13],within:[0,6,8,9,10],without:[5,10,11],wlan0:10,wlevel:11,wlo1:18,wlp3s0:13,word:10,work:[0,1,2,3,4,5,6,7,9,13,14,18],workaround:[1,2,14,15,18],workflow:[9,14],worst:14,would:[10,14,18],wrap:0,wrapper:18,write:[6,9],written:18,wrong:14,wrote:4,x00:[2,4,5],x01:[2,4],x02:[2,4],x03:[2,4],x05:[2,4,5],x06:[2,4],x07:4,x10:2,x14:2,x18:2,x1a:2,x1b:5,x1c:4,x28:2,x30:5,x3c:2,x49:2,x55:2,x58:5,x61:2,x78:4,x84:2,x95:4,xav:17,xe3:4,xe4:4,xid:6,xml:11,xunit:1,ybl:17,yes:13,yet:[5,6,9,10,14],ygx:17,you:[2,3,4,5,6,7,9,10,11,13,15,16,18],your:[2,5,9,10,13,16],yourself:5,zero:13},titles:["Module architecture","changelog","Netlink debug howto","Project contribution guide","Modules layout","Modules in progress","DHCP support","pyroute2","pyroute2 netlink library","IPDB module","IPRoute module","makefile documentation","MPLS howto","Netlink","Netns management overview","Base netlink socket and marshal","report a bug","<no title>","Quickstart"],titleterms:{"case":18,"class":[4,9,15],"default":9,"import":[4,18],The:9,about:10,address:9,algo:13,api:[9,10],architectur:[0,10],arrai:13,async:15,asynchron:15,attribut:13,base:15,basic:13,bridg:9,bug:16,cach:0,chang:5,changelog:1,choke:10,clean:11,clsact:10,commun:5,compat:2,compil:2,context:9,contribut:3,creat:[9,13,14],daemon:5,data:2,debug:[2,4],decod:[2,13],defer:4,develop:[8,11],dhcp:6,differ:9,disciplin:10,disclaim:5,dist:11,doc:11,document:11,doesn:15,drr:10,dump:2,encod:13,enobuf:15,epydoc:11,eventlet:18,except:18,field:[6,13],filter:10,from:9,gener:8,get:9,guid:3,handler:18,help:15,hfsc:10,hood:0,how:10,howto:[2,8,12],htb:10,indic:8,inform:8,instal:[7,11],interfac:[9,14],internet:0,ipdb:[0,9,10,12],iprout:[9,10,12],iproute2:9,ipset:5,ipv4:6,issu:9,kernel:5,label:12,layout:4,librari:8,link:[3,7],list:14,lwtunnel:12,makefil:11,manag:[9,14],mangl:0,marshal:[4,15],messag:[0,4,5,10,13],metric:9,mode:9,modul:[0,4,5,9,10],move:14,mpl:12,multipath:9,namespac:[7,14],netlink:[0,2,8,13,15],netlinksocket:4,netn:14,network:[5,7],nla_map:13,nlmsg_error:10,note:2,nspopen:14,onli:9,option:6,other:[9,11],overview:14,packet:[6,13],pars:13,perform:9,persist:9,port:9,prioriti:9,process:14,progress:5,project:3,protocol:[0,4,6],pyrout:5,pyroute2:[7,8,13],queue:10,quickstart:[9,10,18],read:9,releas:18,remov:14,report:16,requir:[3,7],resourc:18,respons:10,revers:5,rout:[9,12],rtnetlink:7,rule:9,runtim:18,sampl:7,send:13,set:[5,14],signal:18,simplest:7,socket:[0,6,15],softwar:9,spawn:14,special:18,strace:2,submit:5,support:6,syntax:9,tabl:8,target:11,test:[3,11],think:10,thread:[0,10],threadless:10,transact:9,type:13,u32:10,under:0,usag:8,usecas:7,util:4,view:9,when:15,within:14,work:10}})pyroute2-0.4.21/docs/html/_sources/0000775000175000017500000000000013152333366017030 5ustar peetpeet00000000000000pyroute2-0.4.21/docs/html/_sources/general.rst.txt0000664000175000017500000001412513152333361022013 0ustar peetpeet00000000000000pyroute2 ======== Pyroute2 is a pure Python **netlink** library. It requires only Python stdlib, no 3rd party libraries. The library was started as an RTNL protocol implementation, so the name is **pyroute2**, but now it supports many netlink protocols. Some supported netlink families and protocols: * **rtnl**, network settings --- addresses, routes, traffic controls * **nfnetlink** --- netfilter API: **ipset**, **nftables**, ... * **ipq** --- simplest userspace packet filtering, iptables QUEUE target * **devlink** --- manage and monitor devlink-enabled hardware * **generic** --- generic netlink families * **nl80211** --- wireless functions API (basic support) * **taskstats** --- extended process statistics * **acpi_events** --- ACPI events monitoring * **thermal_events** --- thermal events monitoring * **VFS_DQUOT** --- disk quota events monitoring the simplest usecase -------------------- The socket objects, provided by the library, are actual socket objects with a little bit extended API. The additional functionality aims to: * Help to open/bind netlink sockets * Discover generic netlink protocols and multicast groups * Construct, encode and decode netlink messages Maybe the simplest usecase is to monitor events. Disk quota events:: from pyroute2 import DQuotSocket # DQuotSocket automatically performs discovery and binding, # since it has no other functionality beside of the monitoring with DQuotSocket() as ds: for message in ds.get(): print(message) Or IPRoute:: from pyroute2 import IPRoute with IPRoute() as ipr: # With IPRoute objects you have to call bind() manually ipr.bind() for message in ipr.get(): print(message) rtnetlink sample ---------------- More samples you can read in the project documentation. Low-level **IPRoute** utility --- Linux network configuration. The **IPRoute** class is a 1-to-1 RTNL mapping. There are no implicit interface lookups and so on. Some examples:: from socket import AF_INET from pyroute2 import IPRoute # get access to the netlink socket ip = IPRoute() # no monitoring here -- thus no bind() # print interfaces print(ip.get_links()) # create VETH pair and move v0p1 to netns 'test' ip.link_create(ifname='v0p0', peer='v0p1', kind='veth') idx = ip.link_lookup(ifname='v0p1')[0] ip.link('set', index=idx, net_ns_fd='test') # bring v0p0 up and add an address idx = ip.link_lookup(ifname='v0p0')[0] ip.link('set', index=idx, state='up') ip.addr('add', index=idx, address='10.0.0.1', broadcast='10.0.0.255', prefixlen=24) # create a route with metrics ip.route('add', dst='172.16.0.0/24', gateway='10.0.0.10', metrics={'mtu': 1400, 'hoplimit': 16}) # create MPLS lwtunnel # $ sudo modprobe mpls_iptunnel ip.route('add', dst='172.16.0.0/24', oif=idx, encap={'type': 'mpls', 'labels': '200/300'}) # create MPLS route: push label # $ sudo modprobe mpls_router # $ sudo sysctl net.mpls.platform_labels=1024 ip.route('add', family=AF_MPLS, oif=idx, dst=0x200, newdst=[0x200, 0x300]) # release Netlink socket ip.close() High-level transactional interface, **IPDB**, a network settings DB:: from pyroute2 import IPDB # # The `with` statement automatically calls `IPDB.release()` # in the case of an exception. with IPDB() as ip: # # Create bridge and add ports and addresses. # # Transaction will be started by `with` statement # and will be committed at the end of the block with ip.create(kind='bridge', ifname='rhev') as i: i.add_port('em1') i.add_port('em2') i.add_ip('10.0.0.2/24') # --> <-- Here the system state is as described in # the transaction, if no error occurs. If # there is an error, all the changes will be # rolled back. The IPDB arch allows to use it transparently with network namespaces:: from pyroute2 import IPDB from pyroute2 import NetNS # Create IPDB to work with the 'test' ip netns. # # Pls notice, that IPDB itself will work in the # main netns, only the netlink transport is # working in the namespace `test`. ip = IPDB(nl=NetNS('test')) # Wait until someone will set up ipaddr 127.0.0.1 # in the netns on the loopback device ip.interfaces.lo.wait_ip('127.0.0.1') # The IPDB object must be released before exit to # sync all the possible changes that are in progress. 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.7 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.4.21/docs/html/_sources/report.rst.txt0000664000175000017500000000132413152333361021706 0ustar peetpeet00000000000000report a bug ============ In the case you have issues, please report them to the project bug tracker: https://github.com/svinota/pyroute2/issues It is important to provide all the required information with your report: * Linux kernel version * Python version * Specific environment, if used -- gevent, eventlet etc. Sometimes it is needed to measure specific system parameters. There is a code to do that, e.g.:: $ sudo make test-platform Please keep in mind, that this command will try to create and delete different interface types. It is possible also to run the test in your code:: from pprint import pprint from pyroute2.config.test_platform import TestCapsRtnl pprint(TestCapsRtnl().collect()) pyroute2-0.4.21/docs/html/_sources/netns.rst.txt0000664000175000017500000000025113041746270021524 0ustar peetpeet00000000000000.. netns: .. automodule:: pyroute2.netns :members: .. automodule:: pyroute2.netns.nslink :members: .. automodule:: pyroute2.netns.process.proxy :members: pyroute2-0.4.21/docs/html/_sources/devmodules.rst.txt0000664000175000017500000000144613041746270022553 0ustar peetpeet00000000000000.. devmodules: Modules in progress =================== There are several modules in the very initial development state, and the help with them will be particularly valuable. You are more than just welcome to help with: .. automodule:: pyroute2.ipset :members: .. automodule:: pyroute2.iwutil :members: Network settings daemon -- pyrouted ----------------------------------- Pyrouted is a standalone project of a system service, that utilizes the `pyroute2` library. It consists of a daemon controlled by `systemd` and a CLI utility that communicates with the daemon via UNIX socket. * home: https://github.com/svinota/pyrouted * bugs: https://github.com/svinota/pyrouted/issues * pypi: https://pypi.python.org/pypi/pyrouted It is an extremely simple and basic network interface setup tool. pyroute2-0.4.21/docs/html/_sources/netlink.rst.txt0000664000175000017500000000005613112000330022017 0ustar peetpeet00000000000000.. netlink: .. automodule:: pyroute2.netlink pyroute2-0.4.21/docs/html/_sources/iproute.rst.txt0000664000175000017500000000105313112000654022051 0ustar peetpeet00000000000000.. iproute: IPRoute module ============== .. automodule:: pyroute2.iproute :members: Queueing disciplines -------------------- .. automodule:: pyroute2.netlink.rtnl.tcmsg.sched_drr :members: .. automodule:: pyroute2.netlink.rtnl.tcmsg.sched_choke :members: .. automodule:: pyroute2.netlink.rtnl.tcmsg.sched_clsact :members: .. automodule:: pyroute2.netlink.rtnl.tcmsg.sched_hfsc :members: .. automodule:: pyroute2.netlink.rtnl.tcmsg.sched_htb :members: Filters ------- .. automodule:: pyroute2.netlink.rtnl.tcmsg.cls_u32 pyroute2-0.4.21/docs/html/_sources/debug.rst.txt0000664000175000017500000001344013127143031021456 0ustar peetpeet00000000000000.. debug: Netlink debug howto ------------------- Dump data ========= Either run the required command via `strace`, or attach to the running process with `strace -p`. Use `-s {int}` argument to make sure that all the messages are dumped. The `-x` argument instructs `strace` to produce output in the hex format that can be passed to the pyroute2 decoder:: $ strace -e trace=network -x -s 16384 ip ro socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC, NETLINK_ROUTE) = 3 setsockopt(3, SOL_SOCKET, SO_SNDBUF, [32768], 4) = 0 setsockopt(3, SOL_SOCKET, SO_RCVBUF, [1048576], 4) = 0 bind(3, {sa_family=AF_NETLINK, pid=0, groups=00000000}, 12) = 0 getsockname(3, {sa_family=AF_NETLINK, pid=28616, groups=00000000}, [12]) = 0 sendto(3, "\x28\x00\x00\x00\x1a\x00\x01\x03 [skip] ", 40, 0, NULL, 0) = 40 recvmsg(3, {msg_name(12)={sa_family=AF_NETLINK, pid=0, groups=00000000}, msg_iov(1)=[{"\x3c\x00\x00\x00\x18 [skip]", 16384}], msg_controllen=0, msg_flags=0}, 0) = 480 socket(PF_LOCAL, SOCK_DGRAM|SOCK_CLOEXEC, 0) = 4 192.168.122.0/24 dev virbr0 proto kernel scope link src 192.168.122.1 recvmsg(3, {msg_name(12)={sa_family=AF_NETLINK, pid=0, groups=00000000}, msg_iov(1)=[{"\x14\x00\x00\x00\x03 [skip]", 16384}], msg_controllen=0, msg_flags=0}, 0) = 20 +++ exited with 0 +++ Now you can copy `send
()` and `recv
()` buffer strings to a file. Strace compatibility note ========================= Starting with version 4.13, `strace` parses Netlink message headers and displays them in their parsed form instead of displaying the whole buffer in its raw form. The rest of the buffer is still shown, but due to it being incomplete, the method mentioned above doesn't work anymore. For the time being, the easiest workaround is probably to use an older strace version as it only depends on libc6. Decode data =========== The decoder is not provided with rpm or pip packages, so you should have a local git repo of the project:: $ git clone $ cd pyroute2 Now run the decoder:: $ export PYTHONPATH=`pwd` $ python tests/decoder/decoder.py E.g. for the route dump in the file `rt.dump` the command line should be:: $ python tests/decoder/decoder.py \ pyroute2.netlink.rtnl.rtmsg.rtmsg \ rt.dump **Why should I specify the message class?** Why there is no marshalling in the decoder script? 'Cause it is intended to be used with different netlink protocols, not only RTNL, but also nl80211, nfnetlink etc. There is no common marshalling for all the netlink protocols. **How to specify the message class?** All the netlink protocols are defined under `pyroute2/netlink/`, e.g. `rtmsg` module is `pyroute2/netlink/rtnl/rtmsg.py`. Thereafter you should specify the class inside the module, since there can be several classes. In the `rtmsg` case the line will be `pyroute.netlink.rtnl.rtmsg.rtmsg` or, more friendly to the bash autocomplete, `pyroute2/netlink/rtnl/rtmsg.rtmsg`. Notice, that the class you have to specify with dot anyways. **What is the data file format?** Rules are as follows: * The data dump should be in a hex format. Two possible variants are: `\\x00\\x01\\x02\\x03` or `00:01:02:03`. * There can be several packets in the same file. They should be of the same type. * Spaces and line ends are ignored, so you can format the dump as you want. * The `#` symbol starts a comment until the end of the line. * The `#!` symbols start a comment until the end of the file. Example:: # ifinfmsg headers # # nlmsg header \x84\x00\x00\x00 # length \x10\x00 # type \x05\x06 # flags \x49\x61\x03\x55 # sequence number \x00\x00\x00\x00 # pid # RTNL header \x00\x00 # ifi_family \x00\x00 # ifi_type \x00\x00\x00\x00 # ifi_index \x00\x00\x00\x00 # ifi_flags \x00\x00\x00\x00 # ifi_change # ... Compile data ============ Starting with 0.4.1, the library provides `BatchSocket` class, that only compiles and collects requests instead of sending them to the kernel. E.g., it is used by `IPBatch`, that combines `BatchSocket` with `IPRouteMixin`, providing RTNL compiler:: $ python3 Python 3.4.3 (default, Mar 31 2016, 20:42:37) [GCC 5.3.1 20151207 (Red Hat 5.3.1-2)] on linux Type "help", "copyright", "credits" or "license" for more information. # import all the stuff >>> from pyroute2 import IPBatch >>> from pyroute2.common import hexdump # create the compiler >>> ipb = IPBatch() # compile requests into one buffer >>> ipb.link("add", index=550, kind="dummy", ifname="test") >>> ipb.link("set", index=550, state="up") >>> ipb.addr("add", index=550, address="10.0.0.2", mask=24) # inspect the buffer >>> hexdump(ipb.batch) '3c:00:00:00:10:00:05:06:00:00:00:00:a2:7c:00:00:00:00:00:00: 26:02:00:00:00:00:00:00:00:00:00:00:09:00:03:00:74:65:73:74: 00:00:00:00:10:00:12:00:0a:00:01:00:64:75:6d:6d:79:00:00:00: 20:00:00:00:13:00:05:06:00:00:00:00:a2:7c:00:00:00:00:00:00: 26:02:00:00:01:00:00:00:01:00:00:00:28:00:00:00:14:00:05:06: 00:00:00:00:a2:7c:00:00:02:18:00:00:26:02:00:00:08:00:01:00: 0a:00:00:02:08:00:02:00:0a:00:00:02' # reset the buffer >>> ipb.reset() Pls notice, that in Python2 you should use `hexdump(str(ipb.batch))` instead of `hexdump(ipb.batch)`. The data, compiled by `IPBatch` can be used either to run batch requests, when one `send()` call sends several messages at once, or to produce binary buffers to test your own netlink parsers. Or just to dump some data to be sent later and probably even on another host:: >>> ipr = IPRoute() >>> ipr.sendto(ipb.batch, (0, 0)) The compiler always produces requests with `sequence_number == 0`, so if there will be any responses, they can be handled as broadcasts. pyroute2-0.4.21/docs/html/_sources/nlsocket.rst.txt0000664000175000017500000000010612711414357022217 0ustar peetpeet00000000000000.. nlsocket: .. automodule:: pyroute2.netlink.nlsocket :members: pyroute2-0.4.21/docs/html/_sources/arch.rst.txt0000664000175000017500000001421313112000654021301 0ustar peetpeet00000000000000.. sockets: Module architecture ^^^^^^^^^^^^^^^^^^^ Sockets ======= The idea behind the pyroute2 framework is pretty simple. The library provides socket objects, that have: * shortcuts to establish netlink connections * extra methods to run netlink queries * some magic to handle packet bursts * another magic to transparently mangle netlink messages In other sense any netlink socket is just an ordinary socket with `fileno()`, `recv()`, `sendto()` etc. Of course, one can use it in `poll()`. There is an inheritance diagram of netlink sockets, provided by the library: .. inheritance-diagram:: pyroute2.iproute.IPRoute pyroute2.iproute.IPBatch pyroute2.iproute.RawIPRoute pyroute2.iwutil.IW pyroute2.ipset.IPSet pyroute2.netlink.taskstats.TaskStats pyroute2.netlink.ipq.IPQSocket pyroute2.remote.Client pyroute2.remote.RemoteSocket pyroute2.remote.Remote pyroute2.netns.nslink.NetNS :parts: 1 under the hood -------------- Let's assume we use an `IPRoute` object to get the interface list of the system:: from pyroute2 import IPRoute ipr = IPRoute() ipr.get_links() ipr.close() The `get_links()` method is provided by the `IPRouteMixin` class. It chooses the message to send (`ifinfmsg`), prepares required fields and passes it to the next layer:: result.extend(self.nlm_request(msg, RTM_GETLINK, msg_flags)) The `nlm_request()` is a method of the `NetlinkMixin` class. It wraps the pair request/response in one method. The request is done via `put()`, response comes with `get()`. These methods hide under the hood the asynchronous nature of the netlink protocol, where the response can come whenever -- the time and packet order are not guaranteed. But one can use the `sequence_number` field of a netlink message to match responses, and the pair `put()/get()` does it. cache thread ------------ Sometimes it is preferrable to get incoming messages asap and parse them only when there is time for that. For that case the `NetlinkMixin` provides a possibility to start a dedicated cache thread, that will collect and queue incoming messages as they arrive. The thread doesn't affect the socket behaviour: it will behave exactly in the same way, the only difference is that `recv()` will return already cached in the userspace message. To start the thread, one should call `bind()` with `async=True`:: ipr = IPRoute() ipr.bind(async=True) ... # do some stuff ipr.close() message mangling ---------------- An interesting feature of the `IPRSocketMixin` is a netlink proxy code, that allows to register callbacks for different message types. The callback API is simple. The callback must accept the message as a binary data, and must return a dictionary with two keys, `verdict` and `data`. The verdict can be: * for `sendto()`: `forward`, `return` or `error` * for `recv()`: `forward` or `error` E.g.:: msg = ifinfmsg(data) msg.decode() ... # mangle msg msg.reset() msg.encode() return {'verdict': 'forward', 'data': msg.buf.getvalue()} The `error` verdict raises an exception from `data`. The `forward` verdict causes the `data` to be passed. The `return` verdict is valid only in `sendto()` callbacks and means that the `data` should not be passed to the kernel, but instead it must be returned to the user. This magic allows the library to transparently support ovs, teamd, tuntap calls via netlink. The corresponding callbacks transparently route the call to an external utility or to `ioctl()` API. How to register callbacks, see `IPRSocketMixin` init. The `_sproxy` serves `sendto()` mangling, the `_rproxy` serves the `recv()` mangling. Later this API can become public. Netlink messages ================ To handle the data going through the sockets, the library uses different message classes. To create a custom message type, one should inherit: * `nlmsg` to create a netlink message class * `genlmsg` to create generic netlink message class * `nla` to create a NLA class The messages hierarchy: .. inheritance-diagram:: pyroute2.netlink.rtnl.ndmsg.ndmsg pyroute2.netlink.rtnl.tcmsg.tcmsg pyroute2.netlink.rtnl.rtmsg.rtmsg pyroute2.netlink.rtnl.fibmsg.fibmsg pyroute2.netlink.rtnl.ifaddrmsg.ifaddrmsg pyroute2.netlink.rtnl.ifinfmsg.ifinfmsg pyroute2.netlink.rtnl.ifinfmsg.ifinfveth pyroute2.netlink.taskstats.taskstatsmsg pyroute2.netlink.taskstats.tcmd pyroute2.netlink.ctrlmsg pyroute2.netlink.nl80211.nl80211cmd pyroute2.netlink.nfnetlink.ipset.ipset_msg pyroute2.netlink.ipq.ipq_mode_msg pyroute2.netlink.ipq.ipq_packet_msg pyroute2.netlink.ipq.ipq_verdict_msg :parts: 1 IPDB ==== The `IPDB` module implements high-level logic to manage some of the system network settings. It is completely agnostic to the netlink object's nature, the only requirement is that the netlink transport must provide RTNL API. So, using proper mixin classes one can create a custom RTNL-compatible transport. E.g., this way `IPDB` can work over `NetNS` objects, providing the network management within some network namespace — while itself it runs in the main namespace. The `IPDB` architecture is not too complicated, but it implements some useful transaction magic, see `commit()` methods of the `Transactional` objects. .. inheritance-diagram:: pyroute2.ipdb.main.IPDB pyroute2.ipdb.interface.Interface pyroute2.ipdb.linkedset.LinkedSet pyroute2.ipdb.linkedset.IPaddrSet pyroute2.ipdb.route.NextHopSet pyroute2.ipdb.route.Via pyroute2.ipdb.route.Encap pyroute2.ipdb.route.Metrics pyroute2.ipdb.route.BaseRoute pyroute2.ipdb.route.Route pyroute2.ipdb.route.MPLSRoute pyroute2.ipdb.route.RoutingTable pyroute2.ipdb.route.MPLSTable pyroute2.ipdb.route.RoutingTableSet pyroute2.ipdb.rule.Rule pyroute2.ipdb.rule.RuleSet :parts: 1 Internet protocols ================== Beside of the netlink protocols, the library implements a limited set of supplementary internet protocol to play with. .. inheritance-diagram:: pyroute2.protocols.udpmsg pyroute2.protocols.ip4msg pyroute2.protocols.udp4_pseudo_header pyroute2.protocols.ethmsg pyroute2.dhcp.dhcp4msg.dhcp4msg :parts: 1 pyroute2-0.4.21/docs/html/_sources/ipdb.rst.txt0000664000175000017500000000007513041746270021317 0ustar peetpeet00000000000000.. ipdb: .. automodule:: pyroute2.ipdb.main :members: pyroute2-0.4.21/docs/html/_sources/changelog.rst.txt0000664000175000017500000003731413152333361022332 0ustar peetpeet00000000000000changelog ========= * 0.4.21 * ipdb: #416 -- workaround for the vrf_table issue * 0.4.20 * ipdb: #392 -- fix MPLS route key reference * ipdb: #408 -- fix IPv6 routes in tables >= 256 * ipdb: use tos as a route key * 0.4.19 * ipdb: global methods `review()`, `drop()`, `dump()`, `load()` * ipdb: support VLAN protocol (802.1q, 802.1ad) * ipdb: initial support for IPv6 rules * ipdb: #388 -- normalize IPv6 addresses * ipdb: #391 -- support both IPv4 and IPv6 default routes * ipdb: #394 -- correctly work with route priorities * netns: non-destructive `netns.create()` * 0.4.18 * ipdb: #379 [critical] -- routes in global commits * ipdb: #380 -- global commit with disabled plugins * ipdb: #381 -- exceptions fixed * ipdb: #382 -- manage dependent routes during interface commits * ipdb: #384 -- global `review()` * ipdb: #385 -- global `drop()` * netns: #383 -- support ppc64 * general: public API refactored (same signatures; to be documented) * 0.4.17 * req: #374 [critical] -- mode nla init * iproute: #378 [critical] -- fix `flush_routes()` to respect filters * ifinfmsg: #376 -- fix data plugins API to support pyinstaller * 0.4.16 * ipdb: race fixed: remove port/bridge * ipdb: #280 -- race fixed: port/bridge * ipdb: #302 -- ipaddr views: [ifname].ipaddr.ipv4, [ifname]ipaddr.ipv6 * ipdb: #357 -- allow bridge timings to have some delta * ipdb: #338 -- allow to fix interface objects from failed `create()` * rtnl: #336 -- fix vlan flags * iproute: #342 -- the match method takes any callable * nlsocket: #367 -- increase default SO_SNDBUF * ifinfmsg: support tuntap on armv6l, armv7l platforms * 0.4.15 * req: #365 -- full and short nla notation fixed, critical * iproute: #364 -- new method, `brport()` * ipdb: -- support bridge port options * 0.4.14 * event: new genl protocols set: VFS_DQUOT, acpi_event, thermal_event * ipdb: #310 -- fixed priority change on routes * ipdb: #349 -- fix setting ifalias on interfaces * ipdb: #353 -- mitigate kernel oops during bridge creation * ipdb: #354 -- allow to explicitly choose plugins to load * ipdb: #359 -- provide read-only context managers * rtnl: #336 -- vlan flags support * rtnl: #352 -- support interface type plugins * tc: #344 -- mirred action * tc: #346 -- connmark action * netlink: #358 -- memory optimization * config: #360 -- generic asyncio config * iproute: #362 -- allow to change or replace a qdisc * 0.4.13 * ipset: full rework of the IPSET_ATTR_DATA and IPSET_ATTR_ADT ACHTUNG: this commit may break API compatibility * ipset: hash:mac support * ipset: list:set support * ipdb: throw EEXIST when creates VLAN/VXLAN devs with same ID, but under different names * tests: #329 -- include unit tests into the bundle * legal: E/// logo removed * 0.4.12 * ipdb: #314 -- let users choose RTNL groups IPDB listens to * ipdb: #321 -- isolate `net_ns_.*` setup in a separate code block * ipdb: #322 -- IPv6 updates on interfaces in DOWN state * ifinfmsg: allow absolute/relative paths in the net_ns_fd NLA * ipset: #323 -- support setting counters on ipset add * ipset: `headers()` command * ipset: revisions * ipset: #326 -- mark types * 0.4.11 * rtnl: #284 -- support vlan_flags * ipdb: #288 -- do not inore link-local addresses * ipdb: #300 -- sort ip addresses * ipdb: #306 -- support net_ns_pid * ipdb: #307 -- fix IPv6 routes management * ipdb: #311 -- vlan interfaces address loading * iprsocket: #305 -- support NETLINK_LISTEN_ALL_NSID * 0.4.10 * devlink: fix fd leak on broken init * 0.4.9 * sock_diag: initial NETLINK_SOCK_DIAG support * rtnl: fix critical fd leak in the compat code * 0.4.8 * rtnl: compat proxying fix * 0.4.7 * rtnl: compat code is back * netns: custom netns path support * ipset: multiple improvements * 0.4.6 * ipdb: #278 -- fix initial ports mapping * ipset: #277 -- fix ADT attributes parsing * nl80211: #274, #275, #276 -- BSS-related fixes * 0.4.5 * ifinfmsg: GTP interfaces support * generic: devlink protocol support * generic: code cleanup * 0.4.4 * iproute: #262 -- `get_vlans()` fix * iproute: default mask 32 for IPv4 in `addr()` * rtmsg: #260 -- RTA_FLOW support * 0.4.3 * ipdb: #259 -- critical `Interface` class fix * benchmark: initial release * 0.4.2 * ipdb: event modules * ipdb: on-demand views * ipdb: rules management * ipdb: bridge controls * ipdb: #258 -- important Python compatibility fixes * netns: #257 -- pipe leak fix * netlink: support pickling for nlmsg * 0.4.1 * netlink: no buffer copying in the parser * netlink: parse NLA on demand * ipdb: #244 -- lwtunnel multipath fixes * iproute: #235 -- route types * docs updated * 0.4.0 * ACHTUNG: old kernels compatibility code is dropped * ACHTUNG: IPDB uses two separate sockets for monitoring and commands * ipdb: #244 -- multipath lwtunnel * ipdb: #242 -- AF_MPLS routes * ipdb: #241, #234 -- fix create(..., reuse=True) * ipdb: #239 -- route encap and metrics fixed * ipdb: #238 -- generic port management * ipdb: #235 -- support route scope and type * ipdb: #230, #232 -- routes GC (work in progress) * rtnl: #245 -- do not fail if `/proc/net/psched` doesn't exist * rtnl: #233 -- support VRF interfaces (requires net-next) * 0.3.21 * ipdb: #231 -- return `ipdb.common` as deprecated * 0.3.20 * iproute: `vlan_filter()` * iproute: #229 -- FDB management * general: exceptions re-exported via the root module * 0.3.19 * rtmsg: #227 -- MPLS lwtunnel basic support * iproute: `route()` docs updated * general: #228 -- exceptions layout changed * package-rh: rpm subpackages * 0.3.18 * version bump -- include docs in the release tarball * 0.3.17 * tcmsg: qdiscs and filters as plugins * tcmsg: #223 -- tc clsact and bpf direct-action * tcmsg: plug, codel, choke, drr qdiscs * tests: CI in VMs (see civm project) * tests: xunit output * ifinfmsg: tuntap support in i386, i686 * ifinfmsg: #207 -- support vlan filters * examples: #226 -- included in the release tarball * ipdb: partial commits, initial support * 0.3.16 * ipdb: fix the multiple IPs in one commit case * rtnl: support veth peer attributes * netns: support 32bit i686 * netns: fix MIPS support * netns: fix tun/tap creation * netns: fix interface move between namespaces * tcmsg: support hfsc, fq_codel, codel qdiscs * nftables: initial support * netlink: dump/load messages to/from simple types * 0.3.15 * netns: #194 -- fix fd leak * iproute: #184 -- fix routes dump * rtnl: TCA_ACT_BPF support * rtnl: ipvlan support * rtnl: OVS support removed * iproute: rule() improved to support all NLAs * project supported by Ericsson * 0.3.14 * package-rh: spec fixed * package-rh: both licenses added * remote: fixed the setup.py record * 0.3.13 * package-rh: new rpm for Fedora and CentOS * remote: new draft of the remote protocol * netns: refactored using the new remote protocol * ipdb: gretap support * 0.3.12 * ipdb: new `Interface.wait_ip()` routine * ipdb: #175 -- fix `master` attribute cleanup * ipdb: #171 -- support multipath routes * ipdb: memory consumption improvements * rtmsg: MPLS support * rtmsg: RTA_VIA support * iwutil: #174 -- fix FREQ_FIXED flag * 0.3.11 * ipdb: #161 -- fix memory allocations * nlsocket: #161 -- remove monitor mode * 0.3.10 * rtnl: added BPF filters * rtnl: LWtunnel support in ifinfmsg * ipdb: support address attributes * ipdb: global transactions, initial version * ipdb: routes refactored to use key index (speed up) * config: eventlet support embedded (thanks to Angus Lees) * iproute: replace tc classes * iproute: flush_addr(), flush_rules() * iproute: rule() refactored * netns: proxy file objects (stdin, stdout, stderr) * 0.3.9 * root imports: #109, #135 -- `issubclass`, `isinstance` * iwutil: multiple improvements * iwutil: initial tests * proxy: correctly forward NetlinkError * iproute: neighbour tables support * iproute: #147, filters on dump calls * config: initial usage of `capabilities` * 0.3.8 * docs: inheritance diagrams * nlsocket: #126, #132 -- resource deallocation * arch: #128, #131 -- MIPS support * setup.py: #133 -- syntax error during install on Python2 * 0.3.7 * ipdb: new routing syntax * ipdb: sync interface movement between namespaces * ipdb: #125 -- fix route metrics * netns: new class NSPopen * netns: #119 -- i386 syscall * netns: #122 -- return correct errno * netlink: #126 -- fix socket reuse * 0.3.6 * dhcp: initial release DHCPv4 * license: dual GPLv2+ and Apache v2.0 * ovs: port add/delete * macvlan, macvtap: basic support * vxlan: basic support * ipset: basic support * 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.4.21/docs/html/_sources/mpls.rst.txt0000664000175000017500000001022513041746270021352 0ustar peetpeet00000000000000.. mpls: MPLS howto ---------- Short introduction into Linux MPLS. Requirements: * kernel >= 4.4 * modules: `mpls_router`, `mpls_iptunnel` * `$ sudo sysctl net.mpls.platform_labels=$x`, where `$x` -- number of labels * `pyroute2` >= 0.4.0 MPLS labels =========== Possible label formats:: # int "dst": 20 # list of ints "newdst": [20] "newdst": [20, 30] # string "labels": "20/30" Any of these notations should be accepted by `pyroute2`, if not -- try another format and submit an issue to the project github page. The code is quite new, some issues are possible. Refer also to the test cases, there are many usage samples: * `tests/general/test_ipr.py` * `tests/general/test_ipdb.py` IPRoute ======= MPLS routes ~~~~~~~~~~~ Label swap:: from pyroute2 import IPRoute from pyroute2.common import AF_MPLS ipr = IPRoute() # get the `eth0` interface's index: idx = ipr.link_lookup(ifname="eth0")[0] # create the request req = {"family": AF_MPLS, "oif": idx, "dst": 20, "newdst": [30]} # set up the route ipr.route("add", **req) Notice, that `dst` is a single label, while `newdst` is a stack. Label push:: req = {"family": AF_MPLS, "oif": idx, "dst": 20, "newdst": [20, 30]} ipr.route("add", **req) One can set up also the `via` field:: from socket import AF_INET req = {"family": AF_MPLS, "oif": idx, "dst": 20, "newdst": [30], "via": {"family": AF_INET, "addr": "1.2.3.4"}} ipr.route("add", **req) MPLS lwtunnel ~~~~~~~~~~~~~ To inject IP packets into MPLS:: req = {"dst": "1.2.3.0/24", "oif": idx, "encap": {"type": "mpls", "labels": [202, 303]}} ipr.route("add", **req) IPDB ==== MPLS routes ~~~~~~~~~~~ The `IPDB` database also supports MPLS routes, they are reflected in the `ipdb.routes.tables["mpls"]`:: >>> (ipdb ... .routes ... .add({"family": AF_MPLS, ... "oif": ipdb.interfaces["eth0"]["index"], ... "dst": 20, ... "newdst": [30]}) ... .commit()) >>> (ipdb ... .routes ... .add({"family": AF_MPLS, ... "oif": ipdb.interfaces["eth0"]["index"], ... "dst": 22, ... "newdst": [22, 42]}) ... .commit()) >>> ipdb.routes.tables["mpls"].keys() [20, 22] Pls notice, that there is only one MPLS routing table. Multipath MPLS:: with IDPB() as ipdb: (ipdb .routes .add({"family": AF_MPLS, "dst": 20, "multipath": [{"via": {"family": AF_INET, "addr": "10.0.0.2"}, "oif": ipdb.interfaces["eth0"]["index"], "newdst": [30]}, {"via": {"family": AF_INET, "addr": "10.0.0.3"}, "oif": ipdb.interfaces["eth0"]["index"], "newdst": [40]}]}) .commit()) MPLS lwtunnel ~~~~~~~~~~~~~ LWtunnel routes reside in common route tables:: with IPDB() as ipdb: (ipdb .routes .add({"dst": "1.2.3.0/24", "oif": ipdb.interfaces["eth0"]["index"], "encap": {"type": "mpls", "labels": [22]}}) .commit()) print(ipdb.routes["1.2.3.0/24"]) Multipath MPLS lwtunnel:: with IPDB() as ipdb: (ipdb .routes .add({"dst": "1.2.3.0/24", "table": 200, "multipath": [{"oif": ipdb.interfaces["eth0"]["index"], "gateway": "10.0.0.2", "encap": {"type": "mpls", "labels": [200, 300]}}, {"oif": ipdb.interfaces["eth1"]["index"], "gateway": "172.16.0.2", "encap": {"type": "mpls", "labels": [200, 300]}}]}) .commit()) print(ipdb.routes.tables[200]["1.2.3.0/24"]) pyroute2-0.4.21/docs/html/_sources/sm-interfaces.rst.txt0000664000175000017500000000645213141350442023137 0ustar peetpeet00000000000000‹í]ënãžþï§<ÀnŠ•‘ÔuŒ™Eg¢E[LÑÙýÈíh#K†D'ñŸ¢OÓ듔”œ‹ä‹lQT,3˜ÉX>æ‘)ò|çžËO??-"큀Y˜Ä7CŒ¡Fâi„ñüføÛ¯¹ÃŸ?~ Bÿû;Oý…Æ>güÕÍðŽÒå§ëëÇÇG­3Ÿ&)ˆÂÈÈõ¿ý(ò¯Ñõðó@ÓÞøÔç×6W}JÓp²¢D‹ý¹Nüéýln¢b"pBl2,Ïõ ÇtàóœœÎnÒ-»š[vi·ìÂìv™€4õCºÍr’$ñã‚+MW€9ŸlêGl‰úZßbRšÔÜÿ̏²cŸ@qùe·ºsçiÞž%Š=£<†œ»}’4]ÅèkI£?„Y8‰È®»cÚÚðëv†¯>\z¿~\p5£í…‡À%±N‡‡ù* HV³ÌÊ4{FºÛ]×Íz•î؉).UÀ:gùk’n†ÿò ÓÚæo$ŽæOiøðüê͜$“ßɔnŸìoÿø»6ÒŸQŸ²OŒ( Lýƒ›áW£MÛóõ,–L¡ïH8¿£ûÇFÀ6J?°3†sä–ïòUºý,ʪ¡a4c1 £èeÅö4”<Ѻ¥T¡Ùý¹rY¢«è¡Sö(\¿?îQJHò}ӔÄt_~]›ù‹0Z³ÛóãlšetÍE“ÅGÿB¢BéÿV2ž~[knǺ3€[^w†)ÆŽ,ËvíQW·áYblÊØvä*oˆiŽóxAv?K¯;@ÿìïB®åILÓõ-GŠ$®n’çEœsŸÂ$Hds O!mGq±@åf0 ÅaIÇiôšQߑ)šVP}Tg댒…‚ê¡)¬VXý^XýkÊö\˜s}lŽl$ ØnnëKDm Ü€6›± ,"†ÝçˆÚIJïnóÙ9qÎv¹ÖA¬ $ð]’4,K‚CR òžqà=(pOþŠ&iÂ.Vo뀳ûT0–¬Rt µ°Wóù¶òؚ4“,,ç+? €Ÿ™œÛüQp“7.ÀȂv³‘“c9y"œ‚ NöTnØIzJ‚ƒ;€rœrˆÓ4‰ãbèlk¹>¿£Ýùq°Qëh’ûlµ×w™$Ú;> 7ŸÅåÏÂm¯u冎ÇÃ/ §w;ŒW,Š…NsY{ ÚÀCìùì{Àp êÐÌÿÓ_+¶ùŒcÅ"™Vìwa޶°·Ìö–ÛÅÞrÊ{ ýß[®Ú[—°·Úq–žMÙ&ÄÀ+ì7·çö4kl.³Ö*sj)\e·)»MÙmv[yJ3ڠՔ͙Yl« Ÿb±™ò,¶}§¶(荠ܰ 6>?dÌÏGÌlCÈé{lÀ”uä„,ñcAuyÖgLÚùQ ®Vq@flúu)ýrÄôo»ŽŠR§‘çyéŽà%ò€â¹ vùùeŽíÐ=¶dËóµWdžÇ’1ÝF¢Ñª Se˜öß0aàBW]@R¬ÓW^š3Ó8ÅDµËŸµ[1Q™qìqplãÚñ›äÙ ª¹âfª±TXã5Ša-ÀÔC&Ì¡uþ6Ä_ÿù/.³}÷W?úAð£®ýï?ÿU†D†Di=^‚ý`tg?<ä·Ž_ËÁ-œÕ %OT(ACî‰çB‰ý²LîËíƒT"OS²äòŽbÈݖC£/I\€0ª¬CiÂh6SÂH–0‚FÒȓ,ÜB¹›Üìïƒ4zôC&#ØNI¶dj-Ñfi²ÐŸ~SJRrÉSJR¿å’p>ãȒ·ÆÇwr¹„˜’”GÖôA.ÅɋDÒµyø@ŽÕR)LYoå%©гLGœ”À·!=àÏã‘<âç\{žÓç˜?dŽ×CF]D›Ó|; {ˆ<³éL©Ø?uÄ¢ŽXò <¶=Çt;ÿCP€Óy¯œ÷æ{I=^Ây[ÊUÒùø…’žsꅆ®Ò°=\9dêá-$¶aGЃ€á cé#ü±„~G,რ4ÿŸÈ:ŽˆOŠ WRºŽÒ¥y0ù†²ä+m8Ùª„Db• ó>ÁJЏVru ¥†+1F®OLàbsp;•šÞÍIgþ”hA˜ùË%ñÓlŸÉñw‹ÒlŸÚŽEò@¶4?NØ[©ú˜€÷9·lÉFàT))è&ëºešLI–)ý]~Ty+þLøœÅJíN÷€ŠZ¯Ùƒ¯\ÚóÔAKÕW‰‚ÍêŠ@§b{å£Vé*=ð} ‹3Û$]ìláüÀ‚úH*d#È1;GîñzÀÌÝLfŸa[`Ύn,‰ÐÝxôËÀê0»…±‰º Ûr—· µIb·:qš›žgÃ1g sŠœ®[µkîP±9p,èYGxÒO å“j²IU.Ê)¯œòMò<ž€ýè#¶óéÒ}óÏ Ù®ìô{èíœ5«NwлmŒÚ«‹§îa¹J0DÜYUèÀŒ8q.ôœ«Àv”,SÿUÚoö{Œi«Û†xžÅn2=Gnˍ‚Çذ=ÜsÅwϬí!4€…,ˆkJŒGe)5W©¹JÍmšæÂÂb,"¹mÙý7àÆ”5ò}/BÃeÊbó&†Œ’®‡a²…6’6[1§8™EãŒÀ« Üþ‚ce®¶ ,`Ց°-ZOJ‹ñ–‡>§ðPááã!b"GvFbºŸ{‰ý§NËeªž†Ìΰµ”$ü ÀŒ’9Ç¿Oê7Žê¡ë„üÄ9‚ŽE§ÀP¡ÃÓÎ@Üü˜Òê_X] 4Vá ”Qí…}=¹uT]…l‰ŒySÞŒÝU}Š^¥Æçvú*ï|SOR‡ÐP@š€P¡ˆ—êÍk ï…zóš‡çÝç 8EÍ.:£6²ÌíÚ§b«Ü>»œoŸÚtÞTûT­?íSQ ýSQóæ'GTCyã^P Û=o¡Zž® ÕS”“ÒwRØê^©–Jµ(€†ŠöÙIUÔ0r䟷pCœr‰Þ¡‹*²ÅOÛM™%Œ,îV1mž üÜÝn£I›O×{çAÊîÏóÑ* ƒ×%>ÿÄ碈Êw–ïìri¢òUŸs×ùÎõ¥˜ÓBWŒävEajÛŠ+Šƒs«Ž­ã2B7­ã¥7~ˆèx’†ÁœhÉ2ûaNÇ;"ŠWÎ"^!à×o&Q’^•†öƒ`såLÕ[,BªÝ%Éœ*ØÔEGŒTœŠÞÖkBÂÉãÈБÔÄ7Þï ¯àoQžø†ú]²I`ÂTÍŠïµfS”LïI0ž*j7©šMҁ !©¢Mʈy¯¢M‡Ö<ñš-©Òóš­â` !Ð÷úè^Més&+j N2U]tu4öD]uR^p=ô“®˜ñ'¡úø……k6p‰*ÓêôžÃŽsøBý.ÕP™¬ïR¬Â†€]@2iÀoʏ.Y—<\E"øîÑÃõGIXž¢ov µGžË ¯Œ‘µ \hž}òÈӔ,ùóÒµ4‰¢‰?œW¬;:Ï)/Ii./ÕÈZÞ7Fm&O²`r Áä0E› &¯‚©Ümè¹QPÞjèë7å“ïB8yê°¹ßÝ=1O ‘ª59E¯K~Þ̓BL§çI!Ø­Içpë>L»–ÂQ)!Êm Ü)!&ì !ÄDéõ>©Ô²e䃡– 'CKÉL‡ÌÇŽëXcžt ulö㜿bþaðA+Bc4_ V‹ÅúUO|„³'íF» —ÁŒ\Ϟ‡ãá£Wáìù®3j uí>ŒÙCˇ~|Ky.‹PÓçk¡·<•dÙö º*_,ÂS¯>v³Hâ$ïpºm3üÂ"dB\ºÅÀë{ºcä!ÛÀЇéCyÃH5ÞF‘)ƒ¡E±)Þ<Ù.[RÛ'oX"荙ðtMÛÓM`™Ð0û!? w†'t#Jƒ·2T»ògì…öâ›ÕØêÿ}•±™°wý S›µ²$…Q~,$0(\}|‘Jʁۋ_IÁ>JA[<âÏ4eLè&.²–˜‰35,àôÀ¥ëG‘öõoú`‡è"Aq™rwVaŠjAš,—$PŸ^Ùr«Œ\•«÷]eVñ:ò×$ý<(^°¿óÔ_|üQЬrÞâpyroute2-0.4.21/docs/html/_sources/devcontribute.rst.txt0000664000175000017500000000140413041746270023253 0ustar peetpeet00000000000000.. devcontribute: Project contribution guide ========================== To contribute the code to the project, you can use the github instruments: issues and pull-requests. See more on the project github page: https://github.com/svinota/pyroute2 Requirements ++++++++++++ The code should comply with some requirements: * the library must work on Python >= 2.6 and 3.2. * the code must strictly comply with PEP8 (use `flake8`) * the `ctypes` usage must not break the library on SELinux Testing +++++++ To perform code tests, run `make test`. Details about the makefile parameters see in `README.make.md`. Links +++++ * flake8: https://pypi.python.org/pypi/flake8 * vim-flake8: https://github.com/nvie/vim-flake8 * nosetests: http://nose.readthedocs.org/en/latest/ pyroute2-0.4.21/docs/html/_sources/makefile.rst.txt0000664000175000017500000000561713152333361022161 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: epydoc -------------- Build API documentation. Requires `epydoc`. Pls notice that epydoc is discontinued. The reason to support it here is that it performs additional API testing and helps to highlight API inconsistencies. No issues regarding epydoc output format are accepted. target: test ------------ Run tests against current code. Command line options: * python -- path to the Python to use * nosetests -- path to nosetests to use * wlevel -- the Python -W level * coverage -- set `coverage=html` to get coverage report * pdb -- set `pdb=true` to launch pdb on errors * module -- run only specific test module * skip -- skip tests by pattern To run the full test cycle on the project, using a specific python, making html coverage report:: $ sudo make test python=python3 coverage=html To run a specific test module:: $ sudo make test module=general:test_ipdb.py:TestExplicit The module parameter syntax:: ## module=package[:test_file.py[:TestClass[.test_case]]] $ sudo make test module=lnst $ sudo make test module=general:test_ipr.py $ sudo make test module=general:test_ipdb.py:TestExplicit There are several test packages: * general -- common functional tests * eventlet -- Neutron compatibility tests * lnst -- LNST compatibility tests For each package a new Python instance is launched, keep that in mind since it affects the code coverage collection. It is possible to skip tests by a pattern:: $ sudo make test skip=test_stress target: test-ci --------------- Run tests on isolated VMs defined by `tests/ci/configs/*xml`. Requires qemu, kvm, libvirt and civm script: https://github.com/svinota/civm Command line options: * civm -- path to the civm script (if it is not in `$PATH`) target: dist ------------ Make Python distribution package. Command line options: * python -- the Python to use target: install --------------- Build 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 target: develop --------------- Build the package and deploy the egg-link with setuptools. No code will be deployed into the system directories, but instead the local package directory will be visible to the python. In that case one can change the code locally and immediately test it system-wide without running `make install`. * python -- the Python to use other targets ------------- Other targets are either utility targets to be used internally, or hooks for related projects. You can safely ignore them. pyroute2-0.4.21/docs/html/_sources/index.rst.txt0000664000175000017500000000114113112000654021467 0ustar peetpeet00000000000000.. pyroute2 documentation master file .. image:: ericsson.png :align: right pyroute2 netlink library ======================== General information ------------------- .. toctree:: :maxdepth: 2 general changelog makefile report Usage ----- .. toctree:: :maxdepth: 2 usage iproute ipdb netns Howtos ------ .. toctree:: :maxdepth: 2 mpls debug Development ----------- .. toctree:: :maxdepth: 2 devcontribute arch netlink nlsocket Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` pyroute2-0.4.21/docs/html/_sources/usage.rst.txt0000664000175000017500000001024013041746270021500 0ustar peetpeet00000000000000.. usage: Quickstart ========== Runtime ------- In the runtime pyroute2 socket objects behave as normal sockets. One can use them in the poll/select, one can call `recv()` and `sendmsg()`:: from pyroute2 import IPRoute # create RTNL socket ipr = IPRoute() # subscribe to broadcast messages ipr.bind() # wait for data (do not parse it) data = ipr.recv(65535) # parse received data messages = ipr.marshal.parse(data) # shortcut: recv() + parse() # # (under the hood is much more, but for # simplicity it's enough to say so) # messages = ipr.get() But pyroute2 objects have a lot of methods, written to handle specific tasks:: from pyroute2 import IPRoute from pyroute2 import IW # RTNL interface ipr = IPRoute() # WIFI interface iw = IW() # get devices list ipr.get_links() # scan WIFI networks on wlo1 iw.scan(ipr.link_lookup(ifname='wlo1')) More info on specific modules is written in the next chapters. Resource release ---------------- Do not forget to release resources and close sockets. Also keep in mind, that the real fd will be closed only when the Python GC will collect closed objects. Signal handlers --------------- If you place exclusive operations in a signal handler, the locking will not help. The only way to guard the handler is to ignore the signal from the handler:: import signal from pyroute2 import IPDB def handler(signum, frame): # emergency shutdown signal.signal(signal.SIGTERM, signal.SIG_IGN) ipdb.interfaces.test_if.remove().commit() ipdb.release() def main(): with IPDB() as ipdb: signal.signal(signal.SIGTERM, handler) test_if = ipdb.create(ifname='test_if', kind='dummy').commit() ... # do some work Imports ------- The public API is exported by `pyroute2/__init__.py`. There are two main reasons for such approach. First, it is done so to provide a stable API, that will not be affected by changes in the package layout. There can be significant layout changes between versions, but if a symbol is re-exported via `pyroute2/__init__.py`, it will be available with the same import signature. .. warning:: All other objects are also available for import, but they may change signatures in the next versions. E.g.:: # Import a pyroute2 class directly. In the next versions # the import signature can be changed, e.g., NetNS from # pyroute2.netns.nslink it can be moved somewhere else. # from pyroute2.netns.nslink import NetNS ns = NetNS('test') # Import the same class from root module. This signature # will stay the same, any layout change is reflected in # the root module. # from pyroute2 import NetNS ns = NetNS('test') Another function of `pyroute2/__init__.py` is to provide deferred imports. Being imported from the root of the package, classes will be really imported only with the first constructor call. This make possible to change the base of pyroute2 classes on the fly. The proxy class, used in the second case, supports correct `isinstance()` and `issubclass()` semantics, and in both cases the code will work in the same way. There is an exception from the scheme: the exception classes. Exceptions ---------- Since the deferred import provides wrappers, not real classes, one can not use them in `try: ... except: ...` statements. So exception classes are simply reexported here. Developers note: new exceptions modules **must not** import any other pyroute2 modules neither directly, nor indirectly. It means that `__init__.py` files in the import path should not contain pyroute2 symbols referred in the root module as that would cause import error due to recursion. Special cases ============= eventlet -------- The eventlet environment conflicts in some way with socket objects, and pyroute2 provides a workaround for that:: # import symbols # import eventlet from pyroute2 import NetNS from pyroute2.config.eventlet import eventlet_config # setup the environment eventlet.monkey_patch() eventlet_config() # run the code ns = NetNS('nsname') ns.get_routes() ... pyroute2-0.4.21/docs/html/_sources/devgeneral.rst.txt0000664000175000017500000001166113041746270022520 0ustar peetpeet00000000000000.. devgeneral: It is easy to start to develop with pyroute2. In the simplest case one just uses the library as is, and please do not forget to file issues, bugs and feature requests to the project github page. If something should be changed in the library itself, and you think you can do it, this document should help a bit. Modules layout ============== The library consists of several significant parts, and every part has its own functionality:: NetlinkSocket: connects the library to the OS ↑ ↑ | | | ↓ | Marshal ←—→ Message classes | | ↓ NL utility classes: more or less user-friendly API NetlinkSocket and Marshal: :doc:`nlsocket` NetlinkSocket +++++++++++++ Notice, that it is possible to use a custom base class instead of `socket.socket`. Thus, one can transparently port this library to any different transport, or to use it with `eventlet` library, that is not happy with `socket.socket` objects, and so on. Marshal +++++++ A custom marshalling class can be required, if the protocol uses some different marshalling algo from usual netlink. Otherwise it is enough to use `register_policy` method of the `NetlinkSocket`:: # somewhere in a custom netlink class # dict key: message id, int # dict value: message class policy = {IPSET_CMD_PROTOCOL: ipset_msg, IPSET_CMD_LIST: ipset_msg} def __init__(self, ...): ... self.register_policy(policy) But if just matching is not enough, refer to the `Marshal` implementation. It is possible, e.g., to define the custom `fix_message` method to be run on every message, etc. A sample of such custom marshal can be found in the RTNL implementation: `pyroute2.netlink.rtnl`. Messages ++++++++ All the message classes hierarchy is built on the simple fact that the netlink message structure is recursive in that or other way. A usual way to implement messages is described in the netlink docs: :doc:`netlink`. The core module, `pyroute2.netlink`, provides base classes `nlmsg` and `nla`, as well as some other (`genlmsg`), and basic NLA types: `uint32`, `be32`, `ip4addr`, `l2addr` etc. One of the NLA types, `hex`, can be used to dump the NLA structure in the hex format -- it is useful for development. NL utility classes ++++++++++++++++++ They are based on different netlink sockets, such as `IPRsocket` (RTNL), `NL80211` (wireless), or just `NetlinkSocket` -- be it generic netlink or nfnetlink (see taskstats and ipset). Primarily, `pyroute2` is a netlink framework, so basic classes and low-level utilities are intended to return parsed netlink messages, not some user-friendly output. So be not surprised. But user-friendly modules are also possible and partly provided, such as `IPDB`. A list of low-level utility classes: * `IPRoute` [`pyroute2.iproute`], RTNL utility like ip/tc * `IPSet` [`pyroute2.ipset`], manipulate IP sets * `IW` [`pyroute2.iwutil`], basic nl80211 support * `NetNS` [`pyroute2.netns`], netns-enabled `IPRoute` * `TaskStats` [`pyroute2.netlink.taskstats`], taskstats utility High-level utilities: * `IPDB` [`pyroute2.ipdb`], async IP database Deferred imports ++++++++++++++++ The file `pyroute2/__init__.py` is a proxy for some modules, thus providing a fixed import address, like:: from pyroute2 import IPRoute ipr = IPRoute() ... ipr.close() But not only. Actually, `pyroute2/__init__.py` exports not classes and modules, but proxy objects, that load the actual code in the runtime. The rationale is simple: in that way we provide a possibility to use a custom base classes, see `examples/custom_socket_base.py`. Protocol debugging ++++++++++++++++++ The simplest way to start with some netlink protocol is to use a reference implementation. Lets say we wrote the `ipset_msg` class using the kernel code, and want to check how it works. So the ipset(8) utility will be used as a reference implementation:: $ sudo strace -e trace=network -f -x -s 4096 ipset list socket(PF_NETLINK, SOCK_RAW, NETLINK_NETFILTER) = 3 bind(3, {sa_family=AF_NETLINK, pid=0, groups=00000000}, 12) = 0 getsockname(3, {sa_family=AF_NETLINK, pid=7009, groups=00000000}, [12]) = 0 sendto(3, "\x1c\x00\x00\x00\x01\x06\x01\x00\xe3\x95\... recvmsg(3, {msg_name(12)={sa_family=AF_NETLINK, pid=0, groups=00000000}, msg_iov(1)=[{"\x1c\x00\x00\x00\x01\x06\x00\x00\xe3\... sendto(3, "\x1c\x00\x00\x00\x07\x06\x05\x03\xe4\x95\... recvmsg(3, {msg_name(12)={sa_family=AF_NETLINK, pid=0, groups=00000000}, msg_iov(1)=[{"\x78\x00\x00\x00\x07\x06\x02\x00\xe4\... Here you can just copy packet strings from `sendto` and `recvmsg`, place in a file and use `scripts/decoder.py` to inspect them:: $ export PYTHONPATH=`pwd` $ python scripts/decoder.py \ pyroute2.netlink.nfnetlink.ipset.ipset_msg \ scripts/ipset_01.data See collected samples in the `scripts` directory. The script ignores spaces and allows multiple messages in the same file. pyroute2-0.4.21/docs/html/_sources/dhcp.rst.txt0000664000175000017500000000057513041746270021324 0ustar peetpeet00000000000000.. dhcp: DHCP support ============ DHCP support in `pyroute2` is in very initial state, so it is in the «Development section» yet. DHCP protocol has nothing to do with netlink, but `pyroute2` slowly moving from netlink-only library to some more general networking framework. .. automodule:: pyroute2.dhcp :members: .. automodule:: pyroute2.dhcp.dhcp4socket :members: pyroute2-0.4.21/docs/html/arch.html0000664000175000017500000004367613152333364017027 0ustar peetpeet00000000000000 Module architecture — pyroute2 0.4.21 documentation

Module architecture¶

Sockets¶

The idea behind the pyroute2 framework is pretty simple. The library provides socket objects, that have:

  • shortcuts to establish netlink connections
  • extra methods to run netlink queries
  • some magic to handle packet bursts
  • another magic to transparently mangle netlink messages

In other sense any netlink socket is just an ordinary socket with fileno(), recv(), sendto() etc. Of course, one can use it in poll().

There is an inheritance diagram of netlink sockets, provided by the library:

Inheritance diagram of pyroute2.iproute.IPRoute, pyroute2.iproute.IPBatch, pyroute2.iproute.RawIPRoute, pyroute2.iwutil.IW, pyroute2.ipset.IPSet, pyroute2.netlink.taskstats.TaskStats, pyroute2.netlink.ipq.IPQSocket, pyroute2.remote.Client, pyroute2.remote.RemoteSocket, pyroute2.remote.Remote, pyroute2.netns.nslink.NetNS

under the hood¶

Let’s assume we use an IPRoute object to get the interface list of the system:

from pyroute2 import IPRoute
ipr = IPRoute()
ipr.get_links()
ipr.close()

The get_links() method is provided by the IPRouteMixin class. It chooses the message to send (ifinfmsg), prepares required fields and passes it to the next layer:

result.extend(self.nlm_request(msg, RTM_GETLINK, msg_flags))

The nlm_request() is a method of the NetlinkMixin class. It wraps the pair request/response in one method. The request is done via put(), response comes with get(). These methods hide under the hood the asynchronous nature of the netlink protocol, where the response can come whenever – the time and packet order are not guaranteed. But one can use the sequence_number field of a netlink message to match responses, and the pair put()/get() does it.

cache thread¶

Sometimes it is preferrable to get incoming messages asap and parse them only when there is time for that. For that case the NetlinkMixin provides a possibility to start a dedicated cache thread, that will collect and queue incoming messages as they arrive. The thread doesn’t affect the socket behaviour: it will behave exactly in the same way, the only difference is that recv() will return already cached in the userspace message. To start the thread, one should call bind() with async=True:

ipr = IPRoute()
ipr.bind(async=True)
...  # do some stuff
ipr.close()

message mangling¶

An interesting feature of the IPRSocketMixin is a netlink proxy code, that allows to register callbacks for different message types. The callback API is simple. The callback must accept the message as a binary data, and must return a dictionary with two keys, verdict and data. The verdict can be:

  • for sendto(): forward, return or error
  • for recv(): forward or error

E.g.:

msg = ifinfmsg(data)
msg.decode()
...  # mangle msg
msg.reset()
msg.encode()
return {'verdict': 'forward',
        'data': msg.buf.getvalue()}

The error verdict raises an exception from data. The forward verdict causes the data to be passed. The return verdict is valid only in sendto() callbacks and means that the data should not be passed to the kernel, but instead it must be returned to the user.

This magic allows the library to transparently support ovs, teamd, tuntap calls via netlink. The corresponding callbacks transparently route the call to an external utility or to ioctl() API.

How to register callbacks, see IPRSocketMixin init. The _sproxy serves sendto() mangling, the _rproxy serves the recv() mangling. Later this API can become public.

IPDB¶

The IPDB module implements high-level logic to manage some of the system network settings. It is completely agnostic to the netlink object’s nature, the only requirement is that the netlink transport must provide RTNL API.

So, using proper mixin classes one can create a custom RTNL-compatible transport. E.g., this way IPDB can work over NetNS objects, providing the network management within some network namespace — while itself it runs in the main namespace.

The IPDB architecture is not too complicated, but it implements some useful transaction magic, see commit() methods of the Transactional objects.

Internet protocols¶

Beside of the netlink protocols, the library implements a limited set of supplementary internet protocol to play with.

Inheritance diagram of pyroute2.protocols.udpmsg, pyroute2.protocols.ip4msg, pyroute2.protocols.udp4_pseudo_header, pyroute2.protocols.ethmsg, pyroute2.dhcp.dhcp4msg.dhcp4msg
pyroute2-0.4.21/docs/html/_static/0000775000175000017500000000000013152333366016634 5ustar peetpeet00000000000000pyroute2-0.4.21/docs/html/_static/comment-close.png0000664000175000017500000000147513037031774022116 0ustar peetpeet00000000000000‰PNG  IHDRóÿaIDATxmΜõ8†ñ÷$íkó³mÛ¶mÛ¶mÛ¶mÛ¶ÝAïMÎfp×ÿþž:€Oæ®'eð$Q¹qÏ —ÿŽaáÑO[ú€Bæ3‡Uß÷9þO²Ög+ł-Ÿ81dû‡w=7¿ç«ßq“¥ïžöü×1¹ŒŸûCëKa”~ ±ÊŒŽû<ÎÔe͛è‰ÝgË'ÝyÃ{ÝråGn°ä×_öžë.|Ë5œÕ­Ì„Y‡¹ ²l›Ï•§¶¥]’Õ‚‘ŽÓíÒÉ…®O4”‚ƒlØ!AÁ@@àwã²ônŸíyœ^€òÂxa*ž;1’uŸSºÑüWºÝŠO<þ*ë‰7˜g>bž~Ÿyޞ»ü mNÖ³É\£(Žtëë›:+óŒtU&>9Z}æëOã®k=îŠæ®ÛwԈ=Žîe€ãûhjo OÚî÷àSd̳m#Ï(®2ôÚ®‰Šöˆ&ë!Q&Çô<þþ­iá_~ëð Yþ ¡»=ºŒšKâeßšOÄ™Ó Ó)Ó\Ó/&þí—f§ŸûœÙñdðåÇÀ’øUû¶Ì@Æøw‘Ôyq€#ý9œIEND®B`‚pyroute2-0.4.21/docs/html/_static/underscore.js0000664000175000017500000002755413034345740021356 0ustar peetpeet00000000000000// Underscore.js 1.3.1 // (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc. // Underscore is freely distributable under the MIT license. // Portions of Underscore are inspired or borrowed from Prototype, // Oliver Steele's Functional, and John Resig's Micro-Templating. // For all details and documentation: // http://documentcloud.github.com/underscore (function(){function q(a,c,d){if(a===c)return a!==0||1/a==1/c;if(a==null||c==null)return a===c;if(a._chain)a=a._wrapped;if(c._chain)c=c._wrapped;if(a.isEqual&&b.isFunction(a.isEqual))return a.isEqual(c);if(c.isEqual&&b.isFunction(c.isEqual))return c.isEqual(a);var e=l.call(a);if(e!=l.call(c))return false;switch(e){case "[object String]":return a==String(c);case "[object Number]":return a!=+a?c!=+c:a==0?1/a==1/c:a==+c;case "[object Date]":case "[object Boolean]":return+a==+c;case "[object RegExp]":return a.source== c.source&&a.global==c.global&&a.multiline==c.multiline&&a.ignoreCase==c.ignoreCase}if(typeof a!="object"||typeof c!="object")return false;for(var f=d.length;f--;)if(d[f]==a)return true;d.push(a);var f=0,g=true;if(e=="[object Array]"){if(f=a.length,g=f==c.length)for(;f--;)if(!(g=f in a==f in c&&q(a[f],c[f],d)))break}else{if("constructor"in a!="constructor"in c||a.constructor!=c.constructor)return false;for(var h in a)if(b.has(a,h)&&(f++,!(g=b.has(c,h)&&q(a[h],c[h],d))))break;if(g){for(h in c)if(b.has(c, h)&&!f--)break;g=!f}}d.pop();return g}var r=this,G=r._,n={},k=Array.prototype,o=Object.prototype,i=k.slice,H=k.unshift,l=o.toString,I=o.hasOwnProperty,w=k.forEach,x=k.map,y=k.reduce,z=k.reduceRight,A=k.filter,B=k.every,C=k.some,p=k.indexOf,D=k.lastIndexOf,o=Array.isArray,J=Object.keys,s=Function.prototype.bind,b=function(a){return new m(a)};if(typeof exports!=="undefined"){if(typeof module!=="undefined"&&module.exports)exports=module.exports=b;exports._=b}else r._=b;b.VERSION="1.3.1";var j=b.each= b.forEach=function(a,c,d){if(a!=null)if(w&&a.forEach===w)a.forEach(c,d);else if(a.length===+a.length)for(var e=0,f=a.length;e2;a== null&&(a=[]);if(y&&a.reduce===y)return e&&(c=b.bind(c,e)),f?a.reduce(c,d):a.reduce(c);j(a,function(a,b,i){f?d=c.call(e,d,a,b,i):(d=a,f=true)});if(!f)throw new TypeError("Reduce of empty array with no initial value");return d};b.reduceRight=b.foldr=function(a,c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(z&&a.reduceRight===z)return e&&(c=b.bind(c,e)),f?a.reduceRight(c,d):a.reduceRight(c);var g=b.toArray(a).reverse();e&&!f&&(c=b.bind(c,e));return f?b.reduce(g,c,d,e):b.reduce(g,c)};b.find=b.detect= function(a,c,b){var e;E(a,function(a,g,h){if(c.call(b,a,g,h))return e=a,true});return e};b.filter=b.select=function(a,c,b){var e=[];if(a==null)return e;if(A&&a.filter===A)return a.filter(c,b);j(a,function(a,g,h){c.call(b,a,g,h)&&(e[e.length]=a)});return e};b.reject=function(a,c,b){var e=[];if(a==null)return e;j(a,function(a,g,h){c.call(b,a,g,h)||(e[e.length]=a)});return e};b.every=b.all=function(a,c,b){var e=true;if(a==null)return e;if(B&&a.every===B)return a.every(c,b);j(a,function(a,g,h){if(!(e= e&&c.call(b,a,g,h)))return n});return e};var E=b.some=b.any=function(a,c,d){c||(c=b.identity);var e=false;if(a==null)return e;if(C&&a.some===C)return a.some(c,d);j(a,function(a,b,h){if(e||(e=c.call(d,a,b,h)))return n});return!!e};b.include=b.contains=function(a,c){var b=false;if(a==null)return b;return p&&a.indexOf===p?a.indexOf(c)!=-1:b=E(a,function(a){return a===c})};b.invoke=function(a,c){var d=i.call(arguments,2);return b.map(a,function(a){return(b.isFunction(c)?c||a:a[c]).apply(a,d)})};b.pluck= function(a,c){return b.map(a,function(a){return a[c]})};b.max=function(a,c,d){if(!c&&b.isArray(a))return Math.max.apply(Math,a);if(!c&&b.isEmpty(a))return-Infinity;var e={computed:-Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b>=e.computed&&(e={value:a,computed:b})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a))return Math.min.apply(Math,a);if(!c&&b.isEmpty(a))return Infinity;var e={computed:Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;bd?1:0}),"value")};b.groupBy=function(a,c){var d={},e=b.isFunction(c)?c:function(a){return a[c]};j(a,function(a,b){var c=e(a,b);(d[c]||(d[c]=[])).push(a)});return d};b.sortedIndex=function(a, c,d){d||(d=b.identity);for(var e=0,f=a.length;e>1;d(a[g])=0})})};b.difference=function(a){var c=b.flatten(i.call(arguments,1));return b.filter(a,function(a){return!b.include(c,a)})};b.zip=function(){for(var a=i.call(arguments),c=b.max(b.pluck(a,"length")),d=Array(c),e=0;e=0;d--)b=[a[d].apply(this,b)];return b[0]}}; b.after=function(a,b){return a<=0?b():function(){if(--a<1)return b.apply(this,arguments)}};b.keys=J||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var c=[],d;for(d in a)b.has(a,d)&&(c[c.length]=d);return c};b.values=function(a){return b.map(a,b.identity)};b.functions=b.methods=function(a){var c=[],d;for(d in a)b.isFunction(a[d])&&c.push(d);return c.sort()};b.extend=function(a){j(i.call(arguments,1),function(b){for(var d in b)a[d]=b[d]});return a};b.defaults=function(a){j(i.call(arguments, 1),function(b){for(var d in b)a[d]==null&&(a[d]=b[d])});return a};b.clone=function(a){return!b.isObject(a)?a:b.isArray(a)?a.slice():b.extend({},a)};b.tap=function(a,b){b(a);return a};b.isEqual=function(a,b){return q(a,b,[])};b.isEmpty=function(a){if(b.isArray(a)||b.isString(a))return a.length===0;for(var c in a)if(b.has(a,c))return false;return true};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=o||function(a){return l.call(a)=="[object Array]"};b.isObject=function(a){return a===Object(a)}; b.isArguments=function(a){return l.call(a)=="[object Arguments]"};if(!b.isArguments(arguments))b.isArguments=function(a){return!(!a||!b.has(a,"callee"))};b.isFunction=function(a){return l.call(a)=="[object Function]"};b.isString=function(a){return l.call(a)=="[object String]"};b.isNumber=function(a){return l.call(a)=="[object Number]"};b.isNaN=function(a){return a!==a};b.isBoolean=function(a){return a===true||a===false||l.call(a)=="[object Boolean]"};b.isDate=function(a){return l.call(a)=="[object Date]"}; b.isRegExp=function(a){return l.call(a)=="[object RegExp]"};b.isNull=function(a){return a===null};b.isUndefined=function(a){return a===void 0};b.has=function(a,b){return I.call(a,b)};b.noConflict=function(){r._=G;return this};b.identity=function(a){return a};b.times=function(a,b,d){for(var e=0;e/g,">").replace(/"/g,""").replace(/'/g,"'").replace(/\//g,"/")};b.mixin=function(a){j(b.functions(a), function(c){K(c,b[c]=a[c])})};var L=0;b.uniqueId=function(a){var b=L++;return a?a+b:b};b.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var t=/.^/,u=function(a){return a.replace(/\\\\/g,"\\").replace(/\\'/g,"'")};b.template=function(a,c){var d=b.templateSettings,d="var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('"+a.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(d.escape||t,function(a,b){return"',_.escape("+ u(b)+"),'"}).replace(d.interpolate||t,function(a,b){return"',"+u(b)+",'"}).replace(d.evaluate||t,function(a,b){return"');"+u(b).replace(/[\r\n\t]/g," ")+";__p.push('"}).replace(/\r/g,"\\r").replace(/\n/g,"\\n").replace(/\t/g,"\\t")+"');}return __p.join('');",e=new Function("obj","_",d);return c?e(c,b):function(a){return e.call(this,a,b)}};b.chain=function(a){return b(a).chain()};var m=function(a){this._wrapped=a};b.prototype=m.prototype;var v=function(a,c){return c?b(a).chain():a},K=function(a,c){m.prototype[a]= function(){var a=i.call(arguments);H.call(a,this._wrapped);return v(c.apply(b,a),this._chain)}};b.mixin(b);j("pop,push,reverse,shift,sort,splice,unshift".split(","),function(a){var b=k[a];m.prototype[a]=function(){var d=this._wrapped;b.apply(d,arguments);var e=d.length;(a=="shift"||a=="splice")&&e===0&&delete d[0];return v(d,this._chain)}});j(["concat","join","slice"],function(a){var b=k[a];m.prototype[a]=function(){return v(b.apply(this._wrapped,arguments),this._chain)}});m.prototype.chain=function(){this._chain= true;return this};m.prototype.value=function(){return this._wrapped}}).call(this); pyroute2-0.4.21/docs/html/_static/jquery.js0000664000175000017500000025051713037031774020523 0ustar peetpeet00000000000000/*! jQuery v3.1.0 | (c) jQuery Foundation | jquery.org/license */ !function(a,b){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){"use strict";var c=[],d=a.document,e=Object.getPrototypeOf,f=c.slice,g=c.concat,h=c.push,i=c.indexOf,j={},k=j.toString,l=j.hasOwnProperty,m=l.toString,n=m.call(Object),o={};function p(a,b){b=b||d;var c=b.createElement("script");c.text=a,b.head.appendChild(c).parentNode.removeChild(c)}var q="3.1.0",r=function(a,b){return new r.fn.init(a,b)},s=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,t=/^-ms-/,u=/-([a-z])/g,v=function(a,b){return b.toUpperCase()};r.fn=r.prototype={jquery:q,constructor:r,length:0,toArray:function(){return f.call(this)},get:function(a){return null!=a?a<0?this[a+this.length]:this[a]:f.call(this)},pushStack:function(a){var b=r.merge(this.constructor(),a);return b.prevObject=this,b},each:function(a){return r.each(this,a)},map:function(a){return this.pushStack(r.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(f.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(a<0?b:0);return this.pushStack(c>=0&&c0&&b-1 in a)}var x=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C={}.hasOwnProperty,D=[],E=D.pop,F=D.push,G=D.push,H=D.slice,I=function(a,b){for(var c=0,d=a.length;c+~]|"+K+")"+K+"*"),S=new RegExp("="+K+"*([^\\]'\"]*?)"+K+"*\\]","g"),T=new RegExp(N),U=new RegExp("^"+L+"$"),V={ID:new RegExp("^#("+L+")"),CLASS:new RegExp("^\\.("+L+")"),TAG:new RegExp("^("+L+"|[*])"),ATTR:new RegExp("^"+M),PSEUDO:new RegExp("^"+N),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+K+"*(even|odd|(([+-]|)(\\d*)n|)"+K+"*(?:([+-]|)"+K+"*(\\d+)|))"+K+"*\\)|)","i"),bool:new RegExp("^(?:"+J+")$","i"),needsContext:new RegExp("^"+K+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+K+"*((?:-\\d)?\\d*)"+K+"*\\)|)(?=[^-]|$)","i")},W=/^(?:input|select|textarea|button)$/i,X=/^h\d$/i,Y=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,$=/[+~]/,_=new RegExp("\\\\([\\da-f]{1,6}"+K+"?|("+K+")|.)","ig"),aa=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:d<0?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ba=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g,ca=function(a,b){return b?"\0"===a?"\ufffd":a.slice(0,-1)+"\\"+a.charCodeAt(a.length-1).toString(16)+" ":"\\"+a},da=function(){m()},ea=ta(function(a){return a.disabled===!0},{dir:"parentNode",next:"legend"});try{G.apply(D=H.call(v.childNodes),v.childNodes),D[v.childNodes.length].nodeType}catch(fa){G={apply:D.length?function(a,b){F.apply(a,H.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s=b&&b.ownerDocument,w=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==w&&9!==w&&11!==w)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==w&&(l=Z.exec(a)))if(f=l[1]){if(9===w){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(s&&(j=s.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(l[2])return G.apply(d,b.getElementsByTagName(a)),d;if((f=l[3])&&c.getElementsByClassName&&b.getElementsByClassName)return G.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==w)s=b,r=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(ba,ca):b.setAttribute("id",k=u),o=g(a),h=o.length;while(h--)o[h]="#"+k+" "+sa(o[h]);r=o.join(","),s=$.test(a)&&qa(b.parentNode)||b}if(r)try{return G.apply(d,s.querySelectorAll(r)),d}catch(x){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(P,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("fieldset");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&a.sourceIndex-b.sourceIndex;if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return function(b){return"label"in b&&b.disabled===a||"form"in b&&b.disabled===a||"form"in b&&b.disabled===!1&&(b.isDisabled===a||b.isDisabled!==!a&&("label"in b||!ea(b))!==a)}}function pa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function qa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return!!b&&"HTML"!==b.nodeName},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),v!==n&&(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Y.test(n.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}},d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){if("undefined"!=typeof b.getElementsByClassName&&p)return b.getElementsByClassName(a)},r=[],q=[],(c.qsa=Y.test(n.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+K+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+K+"*(?:value|"+J+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){a.innerHTML="";var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+K+"*[*^$|!~]?="),2!==a.querySelectorAll(":enabled").length&&q.push(":enabled",":disabled"),o.appendChild(a).disabled=!0,2!==a.querySelectorAll(":disabled").length&&q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Y.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"*"),s.call(a,"[s!='']:x"),r.push("!=",N)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Y.test(o.compareDocumentPosition),t=b||Y.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?I(k,a)-I(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?I(k,a)-I(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?la(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(S,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&C.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.escape=function(a){return(a+"").replace(ba,ca)},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(_,aa),a[3]=(a[3]||a[4]||a[5]||"").replace(_,aa),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return V.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&T.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(_,aa).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+K+")"+a+"("+K+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:!b||(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(O," ")+" ").indexOf(c)>-1:"|="===b&&(e===c||e.slice(0,c.length+1)===c+"-"))}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=I(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(P,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(_,aa),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return U.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(_,aa).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:oa(!1),disabled:oa(!0),checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return X.test(a.nodeName)},input:function(a){return W.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:pa(function(){return[0]}),last:pa(function(a,b){return[b-1]}),eq:pa(function(a,b,c){return[c<0?c+b:c]}),even:pa(function(a,b){for(var c=0;c=0;)a.push(d);return a}),gt:pa(function(a,b,c){for(var d=c<0?c+b:c;++d1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function va(a,b,c){for(var d=0,e=b.length;d-1&&(f[j]=!(g[j]=l))}}else r=wa(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):G.apply(g,r)})}function ya(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ta(function(a){return a===b},h,!0),l=ta(function(a){return I(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];i1&&ua(m),i>1&&sa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(P,"$1"),c,i0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=E.call(i));u=wa(u)}G.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&ga.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=ya(b[c]),f[u]?d.push(f):e.push(f);f=A(a,za(e,d)),f.selector=a}return f},i=ga.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(_,aa),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=V.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(_,aa),$.test(j[0].type)&&qa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&sa(j),!a)return G.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,!b||$.test(a)&&qa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("fieldset"))}),ja(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){if(!c)return a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){if(!c&&"input"===a.nodeName.toLowerCase())return a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(J,function(a,b,c){var d;if(!c)return a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);r.find=x,r.expr=x.selectors,r.expr[":"]=r.expr.pseudos,r.uniqueSort=r.unique=x.uniqueSort,r.text=x.getText,r.isXMLDoc=x.isXML,r.contains=x.contains,r.escapeSelector=x.escape;var y=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&r(a).is(c))break;d.push(a)}return d},z=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},A=r.expr.match.needsContext,B=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i,C=/^.[^:#\[\.,]*$/;function D(a,b,c){if(r.isFunction(b))return r.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return r.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(C.test(b))return r.filter(b,a,c);b=r.filter(b,a)}return r.grep(a,function(a){return i.call(b,a)>-1!==c&&1===a.nodeType})}r.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?r.find.matchesSelector(d,a)?[d]:[]:r.find.matches(a,r.grep(b,function(a){return 1===a.nodeType}))},r.fn.extend({find:function(a){var b,c,d=this.length,e=this;if("string"!=typeof a)return this.pushStack(r(a).filter(function(){for(b=0;b1?r.uniqueSort(c):c},filter:function(a){return this.pushStack(D(this,a||[],!1))},not:function(a){return this.pushStack(D(this,a||[],!0))},is:function(a){return!!D(this,"string"==typeof a&&A.test(a)?r(a):a||[],!1).length}});var E,F=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,G=r.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||E,"string"==typeof a){if(e="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:F.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof r?b[0]:b,r.merge(this,r.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),B.test(e[1])&&r.isPlainObject(b))for(e in b)r.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}return f=d.getElementById(e[2]),f&&(this[0]=f,this.length=1),this}return a.nodeType?(this[0]=a,this.length=1,this):r.isFunction(a)?void 0!==c.ready?c.ready(a):a(r):r.makeArray(a,this)};G.prototype=r.fn,E=r(d);var H=/^(?:parents|prev(?:Until|All))/,I={children:!0,contents:!0,next:!0,prev:!0};r.fn.extend({has:function(a){var b=r(a,this),c=b.length;return this.filter(function(){for(var a=0;a-1:1===c.nodeType&&r.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?r.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?i.call(r(a),this[0]):i.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(r.uniqueSort(r.merge(this.get(),r(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function J(a,b){while((a=a[b])&&1!==a.nodeType);return a}r.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return y(a,"parentNode")},parentsUntil:function(a,b,c){return y(a,"parentNode",c)},next:function(a){return J(a,"nextSibling")},prev:function(a){return J(a,"previousSibling")},nextAll:function(a){return y(a,"nextSibling")},prevAll:function(a){return y(a,"previousSibling")},nextUntil:function(a,b,c){return y(a,"nextSibling",c)},prevUntil:function(a,b,c){return y(a,"previousSibling",c)},siblings:function(a){return z((a.parentNode||{}).firstChild,a)},children:function(a){return z(a.firstChild)},contents:function(a){return a.contentDocument||r.merge([],a.childNodes)}},function(a,b){r.fn[a]=function(c,d){var e=r.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=r.filter(d,e)),this.length>1&&(I[a]||r.uniqueSort(e),H.test(a)&&e.reverse()),this.pushStack(e)}});var K=/\S+/g;function L(a){var b={};return r.each(a.match(K)||[],function(a,c){b[c]=!0}),b}r.Callbacks=function(a){a="string"==typeof a?L(a):r.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h-1)f.splice(c,1),c<=h&&h--}),this},has:function(a){return a?r.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=g=[],c||b||(f=c=""),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j};function M(a){return a}function N(a){throw a}function O(a,b,c){var d;try{a&&r.isFunction(d=a.promise)?d.call(a).done(b).fail(c):a&&r.isFunction(d=a.then)?d.call(a,b,c):b.call(void 0,a)}catch(a){c.call(void 0,a)}}r.extend({Deferred:function(b){var c=[["notify","progress",r.Callbacks("memory"),r.Callbacks("memory"),2],["resolve","done",r.Callbacks("once memory"),r.Callbacks("once memory"),0,"resolved"],["reject","fail",r.Callbacks("once memory"),r.Callbacks("once memory"),1,"rejected"]],d="pending",e={state:function(){return d},always:function(){return f.done(arguments).fail(arguments),this},"catch":function(a){return e.then(null,a)},pipe:function(){var a=arguments;return r.Deferred(function(b){r.each(c,function(c,d){var e=r.isFunction(a[d[4]])&&a[d[4]];f[d[1]](function(){var a=e&&e.apply(this,arguments);a&&r.isFunction(a.promise)?a.promise().progress(b.notify).done(b.resolve).fail(b.reject):b[d[0]+"With"](this,e?[a]:arguments)})}),a=null}).promise()},then:function(b,d,e){var f=0;function g(b,c,d,e){return function(){var h=this,i=arguments,j=function(){var a,j;if(!(b=f&&(d!==N&&(h=void 0,i=[a]),c.rejectWith(h,i))}};b?k():(r.Deferred.getStackHook&&(k.stackTrace=r.Deferred.getStackHook()),a.setTimeout(k))}}return r.Deferred(function(a){c[0][3].add(g(0,a,r.isFunction(e)?e:M,a.notifyWith)),c[1][3].add(g(0,a,r.isFunction(b)?b:M)),c[2][3].add(g(0,a,r.isFunction(d)?d:N))}).promise()},promise:function(a){return null!=a?r.extend(a,e):e}},f={};return r.each(c,function(a,b){var g=b[2],h=b[5];e[b[1]]=g.add,h&&g.add(function(){d=h},c[3-a][2].disable,c[0][2].lock),g.add(b[3].fire),f[b[0]]=function(){return f[b[0]+"With"](this===f?void 0:this,arguments),this},f[b[0]+"With"]=g.fireWith}),e.promise(f),b&&b.call(f,f),f},when:function(a){var b=arguments.length,c=b,d=Array(c),e=f.call(arguments),g=r.Deferred(),h=function(a){return function(c){d[a]=this,e[a]=arguments.length>1?f.call(arguments):c,--b||g.resolveWith(d,e)}};if(b<=1&&(O(a,g.done(h(c)).resolve,g.reject),"pending"===g.state()||r.isFunction(e[c]&&e[c].then)))return g.then();while(c--)O(e[c],h(c),g.reject);return g.promise()}});var P=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;r.Deferred.exceptionHook=function(b,c){a.console&&a.console.warn&&b&&P.test(b.name)&&a.console.warn("jQuery.Deferred exception: "+b.message,b.stack,c)},r.readyException=function(b){a.setTimeout(function(){throw b})};var Q=r.Deferred();r.fn.ready=function(a){return Q.then(a)["catch"](function(a){r.readyException(a)}),this},r.extend({isReady:!1,readyWait:1,holdReady:function(a){a?r.readyWait++:r.ready(!0)},ready:function(a){(a===!0?--r.readyWait:r.isReady)||(r.isReady=!0,a!==!0&&--r.readyWait>0||Q.resolveWith(d,[r]))}}),r.ready.then=Q.then;function R(){d.removeEventListener("DOMContentLoaded",R),a.removeEventListener("load",R),r.ready()}"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll?a.setTimeout(r.ready):(d.addEventListener("DOMContentLoaded",R),a.addEventListener("load",R));var S=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===r.type(c)){e=!0;for(h in c)S(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0, r.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(r(a),c)})),b))for(;h1,null,!0)},removeData:function(a){return this.each(function(){W.remove(this,a)})}}),r.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=V.get(a,b),c&&(!d||r.isArray(c)?d=V.access(a,b,r.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=r.queue(a,b),d=c.length,e=c.shift(),f=r._queueHooks(a,b),g=function(){r.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return V.get(a,c)||V.access(a,c,{empty:r.Callbacks("once memory").add(function(){V.remove(a,[b+"queue",c])})})}}),r.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length\x20\t\r\n\f]+)/i,ja=/^$|\/(?:java|ecma)script/i,ka={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};ka.optgroup=ka.option,ka.tbody=ka.tfoot=ka.colgroup=ka.caption=ka.thead,ka.th=ka.td;function la(a,b){var c="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&r.nodeName(a,b)?r.merge([a],c):c}function ma(a,b){for(var c=0,d=a.length;c-1)e&&e.push(f);else if(j=r.contains(f.ownerDocument,f),g=la(l.appendChild(f),"script"),j&&ma(g),c){k=0;while(f=g[k++])ja.test(f.type||"")&&c.push(f)}return l}!function(){var a=d.createDocumentFragment(),b=a.appendChild(d.createElement("div")),c=d.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),o.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="",o.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var pa=d.documentElement,qa=/^key/,ra=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,sa=/^([^.]*)(?:\.(.+)|)/;function ta(){return!0}function ua(){return!1}function va(){try{return d.activeElement}catch(a){}}function wa(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)wa(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=ua;else if(!e)return a;return 1===f&&(g=e,e=function(a){return r().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=r.guid++)),a.each(function(){r.event.add(this,b,e,d,c)})}r.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=V.get(a);if(q){c.handler&&(f=c,c=f.handler,e=f.selector),e&&r.find.matchesSelector(pa,e),c.guid||(c.guid=r.guid++),(i=q.events)||(i=q.events={}),(g=q.handle)||(g=q.handle=function(b){return"undefined"!=typeof r&&r.event.triggered!==b.type?r.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(K)||[""],j=b.length;while(j--)h=sa.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n&&(l=r.event.special[n]||{},n=(e?l.delegateType:l.bindType)||n,l=r.event.special[n]||{},k=r.extend({type:n,origType:p,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&r.expr.match.needsContext.test(e),namespace:o.join(".")},f),(m=i[n])||(m=i[n]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,o,g)!==!1||a.addEventListener&&a.addEventListener(n,g)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),r.event.global[n]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=V.hasData(a)&&V.get(a);if(q&&(i=q.events)){b=(b||"").match(K)||[""],j=b.length;while(j--)if(h=sa.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n){l=r.event.special[n]||{},n=(d?l.delegateType:l.bindType)||n,m=i[n]||[],h=h[2]&&new RegExp("(^|\\.)"+o.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&p!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,o,q.handle)!==!1||r.removeEvent(a,n,q.handle),delete i[n])}else for(n in i)r.event.remove(a,n+b[j],c,d,!0);r.isEmptyObject(i)&&V.remove(a,"handle events")}},dispatch:function(a){var b=r.event.fix(a),c,d,e,f,g,h,i=new Array(arguments.length),j=(V.get(this,"events")||{})[b.type]||[],k=r.event.special[b.type]||{};for(i[0]=b,c=1;c-1:r.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h\x20\t\r\n\f]*)[^>]*)\/>/gi,ya=/\s*$/g;function Ca(a,b){return r.nodeName(a,"table")&&r.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a:a}function Da(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function Ea(a){var b=Aa.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function Fa(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(V.hasData(a)&&(f=V.access(a),g=V.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;c1&&"string"==typeof q&&!o.checkClone&&za.test(q))return a.each(function(e){var f=a.eq(e);s&&(b[0]=q.call(this,e,f.html())),Ha(f,b,c,d)});if(m&&(e=oa(b,a[0].ownerDocument,!1,a,d),f=e.firstChild,1===e.childNodes.length&&(e=f),f||d)){for(h=r.map(la(e,"script"),Da),i=h.length;l")},clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=r.contains(a.ownerDocument,a);if(!(o.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||r.isXMLDoc(a)))for(g=la(h),f=la(a),d=0,e=f.length;d0&&ma(g,!i&&la(a,"script")),h},cleanData:function(a){for(var b,c,d,e=r.event.special,f=0;void 0!==(c=a[f]);f++)if(T(c)){if(b=c[V.expando]){if(b.events)for(d in b.events)e[d]?r.event.remove(c,d):r.removeEvent(c,d,b.handle);c[V.expando]=void 0}c[W.expando]&&(c[W.expando]=void 0)}}}),r.fn.extend({detach:function(a){return Ia(this,a,!0)},remove:function(a){return Ia(this,a)},text:function(a){return S(this,function(a){return void 0===a?r.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=a)})},null,a,arguments.length)},append:function(){return Ha(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ca(this,a);b.appendChild(a)}})},prepend:function(){return Ha(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ca(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return Ha(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return Ha(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(r.cleanData(la(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null!=a&&a,b=null==b?a:b,this.map(function(){return r.clone(this,a,b)})},html:function(a){return S(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!ya.test(a)&&!ka[(ia.exec(a)||["",""])[1].toLowerCase()]){a=r.htmlPrefilter(a);try{for(;c1)}});function Xa(a,b,c,d,e){return new Xa.prototype.init(a,b,c,d,e)}r.Tween=Xa,Xa.prototype={constructor:Xa,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||r.easing._default,this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(r.cssNumber[c]?"":"px")},cur:function(){var a=Xa.propHooks[this.prop];return a&&a.get?a.get(this):Xa.propHooks._default.get(this)},run:function(a){var b,c=Xa.propHooks[this.prop];return this.options.duration?this.pos=b=r.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):this.pos=b=a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Xa.propHooks._default.set(this),this}},Xa.prototype.init.prototype=Xa.prototype,Xa.propHooks={_default:{get:function(a){var b;return 1!==a.elem.nodeType||null!=a.elem[a.prop]&&null==a.elem.style[a.prop]?a.elem[a.prop]:(b=r.css(a.elem,a.prop,""),b&&"auto"!==b?b:0)},set:function(a){r.fx.step[a.prop]?r.fx.step[a.prop](a):1!==a.elem.nodeType||null==a.elem.style[r.cssProps[a.prop]]&&!r.cssHooks[a.prop]?a.elem[a.prop]=a.now:r.style(a.elem,a.prop,a.now+a.unit)}}},Xa.propHooks.scrollTop=Xa.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},r.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2},_default:"swing"},r.fx=Xa.prototype.init,r.fx.step={};var Ya,Za,$a=/^(?:toggle|show|hide)$/,_a=/queueHooks$/;function ab(){Za&&(a.requestAnimationFrame(ab),r.fx.tick())}function bb(){return a.setTimeout(function(){Ya=void 0}),Ya=r.now()}function cb(a,b){var c,d=0,e={height:a};for(b=b?1:0;d<4;d+=2-b)c=aa[d],e["margin"+c]=e["padding"+c]=a;return b&&(e.opacity=e.width=a),e}function db(a,b,c){for(var d,e=(gb.tweeners[b]||[]).concat(gb.tweeners["*"]),f=0,g=e.length;f1)},removeAttr:function(a){return this.each(function(){r.removeAttr(this,a)})}}),r.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return"undefined"==typeof a.getAttribute?r.prop(a,b,c):(1===f&&r.isXMLDoc(a)||(e=r.attrHooks[b.toLowerCase()]||(r.expr.match.bool.test(b)?hb:void 0)),void 0!==c?null===c?void r.removeAttr(a,b):e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:(a.setAttribute(b,c+""),c):e&&"get"in e&&null!==(d=e.get(a,b))?d:(d=r.find.attr(a,b),null==d?void 0:d))},attrHooks:{type:{set:function(a,b){if(!o.radioValue&&"radio"===b&&r.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}},removeAttr:function(a,b){var c,d=0,e=b&&b.match(K); if(e&&1===a.nodeType)while(c=e[d++])a.removeAttribute(c)}}),hb={set:function(a,b,c){return b===!1?r.removeAttr(a,c):a.setAttribute(c,c),c}},r.each(r.expr.match.bool.source.match(/\w+/g),function(a,b){var c=ib[b]||r.find.attr;ib[b]=function(a,b,d){var e,f,g=b.toLowerCase();return d||(f=ib[g],ib[g]=e,e=null!=c(a,b,d)?g:null,ib[g]=f),e}});var jb=/^(?:input|select|textarea|button)$/i,kb=/^(?:a|area)$/i;r.fn.extend({prop:function(a,b){return S(this,r.prop,a,b,arguments.length>1)},removeProp:function(a){return this.each(function(){delete this[r.propFix[a]||a]})}}),r.extend({prop:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return 1===f&&r.isXMLDoc(a)||(b=r.propFix[b]||b,e=r.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=r.find.attr(a,"tabindex");return b?parseInt(b,10):jb.test(a.nodeName)||kb.test(a.nodeName)&&a.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),o.optSelected||(r.propHooks.selected={get:function(a){var b=a.parentNode;return b&&b.parentNode&&b.parentNode.selectedIndex,null},set:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex)}}),r.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){r.propFix[this.toLowerCase()]=this});var lb=/[\t\r\n\f]/g;function mb(a){return a.getAttribute&&a.getAttribute("class")||""}r.fn.extend({addClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).addClass(a.call(this,b,mb(this)))});if("string"==typeof a&&a){b=a.match(K)||[];while(c=this[i++])if(e=mb(c),d=1===c.nodeType&&(" "+e+" ").replace(lb," ")){g=0;while(f=b[g++])d.indexOf(" "+f+" ")<0&&(d+=f+" ");h=r.trim(d),e!==h&&c.setAttribute("class",h)}}return this},removeClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).removeClass(a.call(this,b,mb(this)))});if(!arguments.length)return this.attr("class","");if("string"==typeof a&&a){b=a.match(K)||[];while(c=this[i++])if(e=mb(c),d=1===c.nodeType&&(" "+e+" ").replace(lb," ")){g=0;while(f=b[g++])while(d.indexOf(" "+f+" ")>-1)d=d.replace(" "+f+" "," ");h=r.trim(d),e!==h&&c.setAttribute("class",h)}}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):r.isFunction(a)?this.each(function(c){r(this).toggleClass(a.call(this,c,mb(this),b),b)}):this.each(function(){var b,d,e,f;if("string"===c){d=0,e=r(this),f=a.match(K)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else void 0!==a&&"boolean"!==c||(b=mb(this),b&&V.set(this,"__className__",b),this.setAttribute&&this.setAttribute("class",b||a===!1?"":V.get(this,"__className__")||""))})},hasClass:function(a){var b,c,d=0;b=" "+a+" ";while(c=this[d++])if(1===c.nodeType&&(" "+mb(c)+" ").replace(lb," ").indexOf(b)>-1)return!0;return!1}});var nb=/\r/g,ob=/[\x20\t\r\n\f]+/g;r.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=r.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,r(this).val()):a,null==e?e="":"number"==typeof e?e+="":r.isArray(e)&&(e=r.map(e,function(a){return null==a?"":a+""})),b=r.valHooks[this.type]||r.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=r.valHooks[e.type]||r.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(nb,""):null==c?"":c)}}}),r.extend({valHooks:{option:{get:function(a){var b=r.find.attr(a,"value");return null!=b?b:r.trim(r.text(a)).replace(ob," ")}},select:{get:function(a){for(var b,c,d=a.options,e=a.selectedIndex,f="select-one"===a.type,g=f?null:[],h=f?e+1:d.length,i=e<0?h:f?e:0;i-1)&&(c=!0);return c||(a.selectedIndex=-1),f}}}}),r.each(["radio","checkbox"],function(){r.valHooks[this]={set:function(a,b){if(r.isArray(b))return a.checked=r.inArray(r(a).val(),b)>-1}},o.checkOn||(r.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var pb=/^(?:focusinfocus|focusoutblur)$/;r.extend(r.event,{trigger:function(b,c,e,f){var g,h,i,j,k,m,n,o=[e||d],p=l.call(b,"type")?b.type:b,q=l.call(b,"namespace")?b.namespace.split("."):[];if(h=i=e=e||d,3!==e.nodeType&&8!==e.nodeType&&!pb.test(p+r.event.triggered)&&(p.indexOf(".")>-1&&(q=p.split("."),p=q.shift(),q.sort()),k=p.indexOf(":")<0&&"on"+p,b=b[r.expando]?b:new r.Event(p,"object"==typeof b&&b),b.isTrigger=f?2:3,b.namespace=q.join("."),b.rnamespace=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=e),c=null==c?[b]:r.makeArray(c,[b]),n=r.event.special[p]||{},f||!n.trigger||n.trigger.apply(e,c)!==!1)){if(!f&&!n.noBubble&&!r.isWindow(e)){for(j=n.delegateType||p,pb.test(j+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),i=h;i===(e.ownerDocument||d)&&o.push(i.defaultView||i.parentWindow||a)}g=0;while((h=o[g++])&&!b.isPropagationStopped())b.type=g>1?j:n.bindType||p,m=(V.get(h,"events")||{})[b.type]&&V.get(h,"handle"),m&&m.apply(h,c),m=k&&h[k],m&&m.apply&&T(h)&&(b.result=m.apply(h,c),b.result===!1&&b.preventDefault());return b.type=p,f||b.isDefaultPrevented()||n._default&&n._default.apply(o.pop(),c)!==!1||!T(e)||k&&r.isFunction(e[p])&&!r.isWindow(e)&&(i=e[k],i&&(e[k]=null),r.event.triggered=p,e[p](),r.event.triggered=void 0,i&&(e[k]=i)),b.result}},simulate:function(a,b,c){var d=r.extend(new r.Event,c,{type:a,isSimulated:!0});r.event.trigger(d,null,b)}}),r.fn.extend({trigger:function(a,b){return this.each(function(){r.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];if(c)return r.event.trigger(a,b,c,!0)}}),r.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(a,b){r.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),r.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),o.focusin="onfocusin"in a,o.focusin||r.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){r.event.simulate(b,a.target,r.event.fix(a))};r.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=V.access(d,b);e||d.addEventListener(a,c,!0),V.access(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=V.access(d,b)-1;e?V.access(d,b,e):(d.removeEventListener(a,c,!0),V.remove(d,b))}}});var qb=a.location,rb=r.now(),sb=/\?/;r.parseXML=function(b){var c;if(!b||"string"!=typeof b)return null;try{c=(new a.DOMParser).parseFromString(b,"text/xml")}catch(d){c=void 0}return c&&!c.getElementsByTagName("parsererror").length||r.error("Invalid XML: "+b),c};var tb=/\[\]$/,ub=/\r?\n/g,vb=/^(?:submit|button|image|reset|file)$/i,wb=/^(?:input|select|textarea|keygen)/i;function xb(a,b,c,d){var e;if(r.isArray(b))r.each(b,function(b,e){c||tb.test(a)?d(a,e):xb(a+"["+("object"==typeof e&&null!=e?b:"")+"]",e,c,d)});else if(c||"object"!==r.type(b))d(a,b);else for(e in b)xb(a+"["+e+"]",b[e],c,d)}r.param=function(a,b){var c,d=[],e=function(a,b){var c=r.isFunction(b)?b():b;d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(null==c?"":c)};if(r.isArray(a)||a.jquery&&!r.isPlainObject(a))r.each(a,function(){e(this.name,this.value)});else for(c in a)xb(c,a[c],b,e);return d.join("&")},r.fn.extend({serialize:function(){return r.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=r.prop(this,"elements");return a?r.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!r(this).is(":disabled")&&wb.test(this.nodeName)&&!vb.test(a)&&(this.checked||!ha.test(a))}).map(function(a,b){var c=r(this).val();return null==c?null:r.isArray(c)?r.map(c,function(a){return{name:b.name,value:a.replace(ub,"\r\n")}}):{name:b.name,value:c.replace(ub,"\r\n")}}).get()}});var yb=/%20/g,zb=/#.*$/,Ab=/([?&])_=[^&]*/,Bb=/^(.*?):[ \t]*([^\r\n]*)$/gm,Cb=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Db=/^(?:GET|HEAD)$/,Eb=/^\/\//,Fb={},Gb={},Hb="*/".concat("*"),Ib=d.createElement("a");Ib.href=qb.href;function Jb(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(K)||[];if(r.isFunction(c))while(d=f[e++])"+"===d[0]?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function Kb(a,b,c,d){var e={},f=a===Gb;function g(h){var i;return e[h]=!0,r.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function Lb(a,b){var c,d,e=r.ajaxSettings.flatOptions||{};for(c in b)void 0!==b[c]&&((e[c]?a:d||(d={}))[c]=b[c]);return d&&r.extend(!0,a,d),a}function Mb(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===d&&(d=a.mimeType||b.getResponseHeader("Content-Type"));if(d)for(e in h)if(h[e]&&h[e].test(d)){i.unshift(e);break}if(i[0]in c)f=i[0];else{for(e in c){if(!i[0]||a.converters[e+" "+i[0]]){f=e;break}g||(g=e)}f=f||g}if(f)return f!==i[0]&&i.unshift(f),c[f]}function Nb(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}r.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:qb.href,type:"GET",isLocal:Cb.test(qb.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Hb,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":r.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?Lb(Lb(a,r.ajaxSettings),b):Lb(r.ajaxSettings,a)},ajaxPrefilter:Jb(Fb),ajaxTransport:Jb(Gb),ajax:function(b,c){"object"==typeof b&&(c=b,b=void 0),c=c||{};var e,f,g,h,i,j,k,l,m,n,o=r.ajaxSetup({},c),p=o.context||o,q=o.context&&(p.nodeType||p.jquery)?r(p):r.event,s=r.Deferred(),t=r.Callbacks("once memory"),u=o.statusCode||{},v={},w={},x="canceled",y={readyState:0,getResponseHeader:function(a){var b;if(k){if(!h){h={};while(b=Bb.exec(g))h[b[1].toLowerCase()]=b[2]}b=h[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return k?g:null},setRequestHeader:function(a,b){return null==k&&(a=w[a.toLowerCase()]=w[a.toLowerCase()]||a,v[a]=b),this},overrideMimeType:function(a){return null==k&&(o.mimeType=a),this},statusCode:function(a){var b;if(a)if(k)y.always(a[y.status]);else for(b in a)u[b]=[u[b],a[b]];return this},abort:function(a){var b=a||x;return e&&e.abort(b),A(0,b),this}};if(s.promise(y),o.url=((b||o.url||qb.href)+"").replace(Eb,qb.protocol+"//"),o.type=c.method||c.type||o.method||o.type,o.dataTypes=(o.dataType||"*").toLowerCase().match(K)||[""],null==o.crossDomain){j=d.createElement("a");try{j.href=o.url,j.href=j.href,o.crossDomain=Ib.protocol+"//"+Ib.host!=j.protocol+"//"+j.host}catch(z){o.crossDomain=!0}}if(o.data&&o.processData&&"string"!=typeof o.data&&(o.data=r.param(o.data,o.traditional)),Kb(Fb,o,c,y),k)return y;l=r.event&&o.global,l&&0===r.active++&&r.event.trigger("ajaxStart"),o.type=o.type.toUpperCase(),o.hasContent=!Db.test(o.type),f=o.url.replace(zb,""),o.hasContent?o.data&&o.processData&&0===(o.contentType||"").indexOf("application/x-www-form-urlencoded")&&(o.data=o.data.replace(yb,"+")):(n=o.url.slice(f.length),o.data&&(f+=(sb.test(f)?"&":"?")+o.data,delete o.data),o.cache===!1&&(f=f.replace(Ab,""),n=(sb.test(f)?"&":"?")+"_="+rb++ +n),o.url=f+n),o.ifModified&&(r.lastModified[f]&&y.setRequestHeader("If-Modified-Since",r.lastModified[f]),r.etag[f]&&y.setRequestHeader("If-None-Match",r.etag[f])),(o.data&&o.hasContent&&o.contentType!==!1||c.contentType)&&y.setRequestHeader("Content-Type",o.contentType),y.setRequestHeader("Accept",o.dataTypes[0]&&o.accepts[o.dataTypes[0]]?o.accepts[o.dataTypes[0]]+("*"!==o.dataTypes[0]?", "+Hb+"; q=0.01":""):o.accepts["*"]);for(m in o.headers)y.setRequestHeader(m,o.headers[m]);if(o.beforeSend&&(o.beforeSend.call(p,y,o)===!1||k))return y.abort();if(x="abort",t.add(o.complete),y.done(o.success),y.fail(o.error),e=Kb(Gb,o,c,y)){if(y.readyState=1,l&&q.trigger("ajaxSend",[y,o]),k)return y;o.async&&o.timeout>0&&(i=a.setTimeout(function(){y.abort("timeout")},o.timeout));try{k=!1,e.send(v,A)}catch(z){if(k)throw z;A(-1,z)}}else A(-1,"No Transport");function A(b,c,d,h){var j,m,n,v,w,x=c;k||(k=!0,i&&a.clearTimeout(i),e=void 0,g=h||"",y.readyState=b>0?4:0,j=b>=200&&b<300||304===b,d&&(v=Mb(o,y,d)),v=Nb(o,v,y,j),j?(o.ifModified&&(w=y.getResponseHeader("Last-Modified"),w&&(r.lastModified[f]=w),w=y.getResponseHeader("etag"),w&&(r.etag[f]=w)),204===b||"HEAD"===o.type?x="nocontent":304===b?x="notmodified":(x=v.state,m=v.data,n=v.error,j=!n)):(n=x,!b&&x||(x="error",b<0&&(b=0))),y.status=b,y.statusText=(c||x)+"",j?s.resolveWith(p,[m,x,y]):s.rejectWith(p,[y,x,n]),y.statusCode(u),u=void 0,l&&q.trigger(j?"ajaxSuccess":"ajaxError",[y,o,j?m:n]),t.fireWith(p,[y,x]),l&&(q.trigger("ajaxComplete",[y,o]),--r.active||r.event.trigger("ajaxStop")))}return y},getJSON:function(a,b,c){return r.get(a,b,c,"json")},getScript:function(a,b){return r.get(a,void 0,b,"script")}}),r.each(["get","post"],function(a,b){r[b]=function(a,c,d,e){return r.isFunction(c)&&(e=e||d,d=c,c=void 0),r.ajax(r.extend({url:a,type:b,dataType:e,data:c,success:d},r.isPlainObject(a)&&a))}}),r._evalUrl=function(a){return r.ajax({url:a,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},r.fn.extend({wrapAll:function(a){var b;return this[0]&&(r.isFunction(a)&&(a=a.call(this[0])),b=r(a,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstElementChild)a=a.firstElementChild;return a}).append(this)),this},wrapInner:function(a){return r.isFunction(a)?this.each(function(b){r(this).wrapInner(a.call(this,b))}):this.each(function(){var b=r(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=r.isFunction(a);return this.each(function(c){r(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(a){return this.parent(a).not("body").each(function(){r(this).replaceWith(this.childNodes)}),this}}),r.expr.pseudos.hidden=function(a){return!r.expr.pseudos.visible(a)},r.expr.pseudos.visible=function(a){return!!(a.offsetWidth||a.offsetHeight||a.getClientRects().length)},r.ajaxSettings.xhr=function(){try{return new a.XMLHttpRequest}catch(b){}};var Ob={0:200,1223:204},Pb=r.ajaxSettings.xhr();o.cors=!!Pb&&"withCredentials"in Pb,o.ajax=Pb=!!Pb,r.ajaxTransport(function(b){var c,d;if(o.cors||Pb&&!b.crossDomain)return{send:function(e,f){var g,h=b.xhr();if(h.open(b.type,b.url,b.async,b.username,b.password),b.xhrFields)for(g in b.xhrFields)h[g]=b.xhrFields[g];b.mimeType&&h.overrideMimeType&&h.overrideMimeType(b.mimeType),b.crossDomain||e["X-Requested-With"]||(e["X-Requested-With"]="XMLHttpRequest");for(g in e)h.setRequestHeader(g,e[g]);c=function(a){return function(){c&&(c=d=h.onload=h.onerror=h.onabort=h.onreadystatechange=null,"abort"===a?h.abort():"error"===a?"number"!=typeof h.status?f(0,"error"):f(h.status,h.statusText):f(Ob[h.status]||h.status,h.statusText,"text"!==(h.responseType||"text")||"string"!=typeof h.responseText?{binary:h.response}:{text:h.responseText},h.getAllResponseHeaders()))}},h.onload=c(),d=h.onerror=c("error"),void 0!==h.onabort?h.onabort=d:h.onreadystatechange=function(){4===h.readyState&&a.setTimeout(function(){c&&d()})},c=c("abort");try{h.send(b.hasContent&&b.data||null)}catch(i){if(c)throw i}},abort:function(){c&&c()}}}),r.ajaxPrefilter(function(a){a.crossDomain&&(a.contents.script=!1)}),r.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(a){return r.globalEval(a),a}}}),r.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET")}),r.ajaxTransport("script",function(a){if(a.crossDomain){var b,c;return{send:function(e,f){b=r("

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']

Responses¶

The pyroute2 netlink socket implementation is agnostic to the particular netlink protocols, and always returns a list of messages as the response to a request sent to the kernel:

# this request returns one match
eth0 = ipr.link_lookup(ifname='eth0')
len(eth0)  # -> 1, if exists, else 0

# but that one returns a set of
up = ipr.link_lookup(operstate='UP')
len(up)  # -> k, where 0 <= k <= [interface count]

Thus, always expect a list in the response, running any IPRoute() netlink request.

NLMSG_ERROR responses¶

Some kernel subsystems return NLMSG_ERROR in the response to any request. It is OK as long as nlmsg[“header”][“error”] is None. Otherwise an exception will be raised by the parser.

So if instead of an exception you get a NLMSG_ERROR message, it means error == 0, the same as $? == 0 in bash.

How to work with messages¶

Every netlink message contains header, fields and NLAs (netlink attributes). Every NLA is a netlink message... (see “recursion”).

And the library provides parsed messages according to this scheme. Every RTNL message contains:

  • nlmsg[‘header’] – parsed header
  • nlmsg[‘attrs’] – NLA chain (parsed on demand)
  • 0 .. k data fields, e.g. nlmsg[‘flags’] etc.
  • nlmsg.header – the header fields spec
  • nlmsg.fields – the data fields spec
  • nlmsg.nla_map – NLA spec

An important parser feature is that NLAs are parsed on demand, when someone tries to access them. Otherwise the parser doesn’t waste CPU cycles.

The NLA chain is a list-like structure, not a dictionary. The netlink standard doesn’t require NLAs to be unique within one message:

{'__align': (),
 'attrs': [('IFLA_IFNAME', 'lo'),    # [1]
           ('IFLA_TXQLEN', 1),
           ('IFLA_OPERSTATE', 'UNKNOWN'),
           ('IFLA_LINKMODE', 0),
           ('IFLA_MTU', 65536),
           ('IFLA_GROUP', 0),
           ('IFLA_PROMISCUITY', 0),
           ('IFLA_NUM_TX_QUEUES', 1),
           ('IFLA_NUM_RX_QUEUES', 1),
           ('IFLA_CARRIER', 1),
           ...],
 'change': 0,
 'event': 'RTM_NEWLINK',             # [2]
 'family': 0,
 'flags': 65609,
 'header': {'error': None,           # [3]
            'flags': 2,
            'length': 1180,
            'pid': 28233,
            'sequence_number': 257,  # [4]
            'type': 16},             # [5]
 'ifi_type': 772,
 'index': 1}

 # [1] every NLA is parsed upon access
 # [2] this field is injected by the RTNL parser
 # [3] if not None, an exception will be raised
 # [4] more details in the netlink description
 # [5] 16 == RTM_NEWLINK

To access fields:

msg['index'] == 1

To access one NLA:

msg.get_attr('IFLA_CARRIER') == 1

When the NLA with the specified name is not present in the chain, get_attr() returns None. To get the list of all NLAs of that name, use get_attrs(). A real example with NLA hierarchy, take notice of get_attr() and get_attrs() usage:

# for macvlan interfaces there may be several
# IFLA_MACVLAN_MACADDR NLA provided, so use
# get_attrs() to get all the list, not only
# the first one

(msg
 .get_attr('IFLA_LINKINFO')           # one NLA
 .get_attr('IFLA_INFO_DATA')          # one NLA
 .get_attrs('IFLA_MACVLAN_MACADDR'))  # a list of

Pls read carefully the message structure prior to start the coding.

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.

API¶

class pyroute2.iproute.IPBatch(fileno=None, all_ns=False)¶

Netlink requests compiler. Does not send any requests, but instead stores them in the internal binary buffer. The contents of the buffer can be used to send batch requests, to test custom netlink parsers and so on.

Uses IPRouteMixin and provides all the same API as normal IPRoute objects:

# create the batch compiler
ipb = IPBatch()
# compile requests into the internal buffer
ipb.link("add", index=550, ifname="test", kind="dummy")
ipb.link("set", index=550, state="up")
ipb.addr("add", index=550, address="10.0.0.2", mask=24)
# save the buffer
data = ipb.batch
# reset the buffer
ipb.reset()
...
# send the buffer
IPRoute().sendto(data, (0, 0))
class pyroute2.iproute.IPRoute(fileno=None, all_ns=False)¶

Public class that provides RTNL API to the current network namespace.

class pyroute2.iproute.IPRouteMixin¶

IPRouteMixin should not be instantiated by itself. It is intended to be used as a mixin class that provides RTNL API. Following classes use IPRouteMixin:

  • IPRoute – RTNL API to the current network namespace
  • NetNS – RTNL API to another network namespace
  • IPBatch – RTNL compiler

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()
# create an interface
ipr.link('add', ifname='brx', kind='bridge')
# lookup the index
dev = ipr.link_lookup(ifname='brx')[0]
# bring it down
ipr.link('set', index=dev, state='down')
# change the interface MAC address and rename it just for fun
ipr.link('set', index=dev,
         address='00:11:22:33:44:55',
         ifname='br-ctrl')
# add primary IP address
ipr.addr('add', index=dev,
         address='10.0.0.1', mask=24,
         broadcast='10.0.0.255')
# add secondary IP address
ipr.addr('add', index=dev,
         address='10.0.0.2', mask=24,
         broadcast='10.0.0.255')
# bring it up
ipr.link('set', index=dev, state='up')
addr(command, index=None, address=None, mask=None, family=None, scope=None, match=None, **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
  • **kwarg – any ifaddrmsg field or NLA

Later the method signature will be changed to:

def addr(self, command, match=None, **kwarg):
    # the method body

So only keyword arguments (except of the command) will be accepted. The reason for this change is an unification of API.

Example:

idx = 62
ip.addr('add', index=idx, address='10.0.0.1', mask=24)
ip.addr('add', index=idx, address='10.0.0.2', mask=24)

With more NLAs:

# explicitly set broadcast address
ip.addr('add', index=idx,
        address='10.0.0.3',
        broadcast='10.0.0.255',
        prefixlen=24)

# make the secondary address visible to ifconfig: add label
ip.addr('add', index=idx,
        address='10.0.0.4',
        broadcast='10.0.0.255',
        prefixlen=24,
        label='eth0:1')

Configure p2p address on an interface:

ip.addr('add', index=idx,
        address='10.1.1.2',
        mask=24,
        local='10.1.1.1')
brport(command, **kwarg)¶

Set bridge port parameters. Example:

idx = ip.link_lookup(ifname='eth0')
ip.brport("set", index=idx, unicast_flood=0, cost=200)
ip.brport("show", index=idx)

Possible keywords are NLA names for the protinfo_bridge class, without the prefix and in lower letters.

fdb(command, **kwarg)¶

Bridge forwarding database management.

More details:
  • kernel:Documentation/networking/switchdev.txt
  • pyroute2.netlink.rtnl.ndmsg

add

Add a new FDB record. Works in the same way as ARP cache management, but some additional NLAs can be used:

# simple FDB record
#
ip.fdb('add',
       ifindex=ip.link_lookup(ifname='br0')[0],
       lladdr='00:11:22:33:44:55',
       dst='10.0.0.1')

# specify vlan
# NB: vlan should exist on the device, use
# `vlan_filter()`
#
ip.fdb('add',
       ifindex=ip.link_lookup(ifname='br0')[0],
       lladdr='00:11:22:33:44:55',
       dst='10.0.0.1',
       vlan=200)

# specify vxlan id and port
# NB: works only for vxlan devices, use
# `link("add", kind="vxlan", ...)`
#
# if port is not specified, the default one is used
# by the kernel.
#
# if vni (vxlan id) is equal to the device vni,
# the kernel doesn't report it back
#
ip.fdb('add',
       ifindex=ip.link_lookup(ifname='vx500')[0]
       lladdr='00:11:22:33:44:55',
       dst='10.0.0.1',
       port=5678,
       vni=600)

append

Append a new FDB record. The same syntax as for add.

del

Remove an existing FDB record. The same syntax as for add.

dump

Dump all the FDB records. If any **kwarg is provided, results will be filtered:

# dump all the records
ip.fdb('dump')

# show only specific lladdr, dst, vlan etc.
ip.fdb('dump', lladdr='00:11:22:33:44:55')
ip.fdb('dump', dst='10.0.0.1')
ip.fdb('dump', vlan=200)
flush_addr(*argv, **kwarg)¶

Flush IP addresses.

Examples:

# flush all addresses on the interface with index 2:
ipr.flush_addr(index=2)

# flush all addresses with IFA_LABEL='eth0':
ipr.flush_addr(label='eth0')
flush_routes(*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().

flush_rules(*argv, **kwarg)¶

Flush rules. Please keep in mind, that by default the function operates on all rules of all families. To work only on IPv4 rules, one should explicitly specify family=AF_INET.

Examples:

# flush all IPv4 rule with priorities above 5 and below 32000
ipr.flush_rules(family=AF_INET, priority=lambda x: 5 < x < 32000)

# flush all IPv6 rules that point to table 250:
ipr.flush_rules(family=socket.AF_INET6, table=250)
get_addr(family=0, match=None, **kwarg)¶

Dump addresses.

If family is not specified, both AF_INET and AF_INET6 addresses will be dumped:

# get all addresses
ip.get_addr()

It is possible to apply filters on the results:

# get addresses for the 2nd interface
ip.get_addr(index=2)

# get addresses with IFA_LABEL == 'eth0'
ip.get_addr(label='eth0')

# get all the subnet addresses on the interface, identified
# by broadcast address (should be explicitly specified upon
# creation)
ip.get_addr(index=2, broadcast='192.168.1.255')

A custom predicate can be used as a filter:

ip.get_addr(match=lambda x: x['index'] == 1)
get_classes(index=0)¶

Get classes for specified interface.

get_default_routes(family=0, table=254)¶

Get default routes

get_filters(index=0, handle=0, parent=0)¶

Get filters for specified interface, handle and parent.

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)
get_neighbors(family=0)¶

Alias of get_neighbours(), deprecated.

get_neighbours(family=0, match=None, **kwarg)¶

Dump ARP cache records.

The family keyword sets the family for the request: e.g. AF_INET or AF_INET6 for arp cache, AF_BRIDGE for fdb.

If other keyword arguments not empty, they are used as filter. Also, one can explicitly set filter as a function with the match parameter.

Examples:

# get neighbours on the 3rd link:
ip.get_neighbours(ifindex=3)

# get a particular record by dst:
ip.get_neighbours(dst='172.16.0.1')

# get fdb records:
ip.get_neighbours(AF_BRIDGE)

# and filter them by a function:
ip.get_neighbours(AF_BRIDGE, match=lambda x: x['state'] == 2)
get_ntables(family=0)¶

Get neighbour tables

get_qdiscs(index=None)¶

Get all queue disciplines for all interfaces or for specified one.

get_routes(family=0, match=None, **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
get_rules(family=0, match=None, **kwarg)¶

Get all rules. By default return all rules. To explicitly request the IPv4 rules use family=AF_INET.

Example::
ip.get_rules() # get all the rules for all families ip.get_rules(family=AF_INET6) # get only IPv6 rules
get_vlans(**kwarg)¶

Dump available vlan info on bridge ports

Link operations.

Keywords to set up ifinfmsg fields:
  • index – interface index
  • family – AF_BRIDGE for bridge operations, otherwise 0
  • flags – device flags
  • change – change mask

All other keywords will be translated to NLA names, e.g. mtu -> IFLA_MTU, af_spec -> IFLA_AF_SPEC etc. You can provide a complete NLA structure or let filters do it for you. E.g., these pairs show equal statements:

# set device MTU
ip.link("set", index=x, mtu=1000)
ip.link("set", index=x, IFLA_MTU=1000)

# add vlan device
ip.link("add", ifname="test", kind="dummy")
ip.link("add", ifname="test",
        IFLA_LINKINFO={'attrs': [['IFLA_INFO_KIND', 'dummy']]})

Filters are implemented in the pyroute2.netlink.rtnl.req module. You can contribute your own if you miss shortcuts.

Commands:

add

To create an interface, one should specify the interface kind:

ip.link("add",
        ifname="test",
        kind="dummy")

The kind can be any of those supported by kernel. It can be dummy, bridge, bond etc. On modern kernels one can specify even interface index:

ip.link("add",
        ifname="br-test",
        kind="bridge",
        index=2345)

Specific type notes:

► gre

Create GRE tunnel:

ip.link("add",
        ifname="grex",
        kind="gre",
        gre_local="172.16.0.1",
        gre_remote="172.16.0.101",
        gre_ttl=16)

The keyed GRE requires explicit iflags/oflags specification:

ip.link("add",
        ifname="grex",
        kind="gre",
        gre_local="172.16.0.1",
        gre_remote="172.16.0.101",
        gre_ttl=16,
        gre_ikey=10,
        gre_okey=10,
        gre_iflags=32,
        gre_oflags=32)

Support for GRE over IPv6 is also included; use kind=ip6gre and ip6gre_ as the prefix for its values.

► macvlan

Macvlan interfaces act like VLANs within OS. The macvlan driver provides an ability to add several MAC addresses on one interface, where every MAC address is reflected with a virtual interface in the system.

In some setups macvlan interfaces can replace bridge interfaces, providing more simple and at the same time high-performance solution:

ip.link("add",
        ifname="mvlan0",
        kind="macvlan",
        link=ip.link_lookup(ifname="em1")[0],
        macvlan_mode="private").commit()

Several macvlan modes are available: “private”, “vepa”, “bridge”, “passthru”. Ususally the default is “vepa”.

► macvtap

Almost the same as macvlan, but creates also a character tap device:

ip.link("add",
        ifname="mvtap0",
        kind="macvtap",
        link=ip.link_lookup(ifname="em1")[0],
        macvtap_mode="vepa").commit()

Will create a device file “/dev/tap%s” % index

► tuntap

Possible tuntap keywords:

  • mode — “tun” or “tap”
  • uid — integer
  • gid — integer
  • ifr — dict of tuntap flags (see ifinfmsg:... tuntap_data)

Create a tap interface:

ip.link("add",
        ifname="tap0",
        kind="tuntap",
        mode="tap")

Tun/tap interfaces are created using ioctl(), but the library provides a transparent way to manage them using netlink API.

► veth

To properly create veth interface, one should specify peer also, since veth interfaces are created in pairs:

ip.link("add", ifname="v1p0", kind="veth", peer="v1p1")

► vlan

VLAN interfaces require additional parameters, vlan_id and link, where link is a master interface to create VLAN on:

ip.link("add",
        ifname="v100",
        kind="vlan",
        link=ip.link_lookup(ifname="eth0")[0],
        vlan_id=100)

There is a possibility to create also 802.1ad interfaces:

# create external vlan 802.1ad, s-tag
ip.link("add",
        ifname="v100s",
        kind="vlan",
        link=ip.link_lookup(ifname="eth0")[0],
        vlan_id=100,
        vlan_protocol=0x88a8)

# create internal vlan 802.1q, c-tag
ip.link("add",
        ifname="v100c",
        kind="vlan",
        link=ip.link_lookup(ifname="v100s")[0],
        vlan_protocol=0x8100)

► vrf

VRF interfaces (see linux/Documentation/networking/vrf.txt):

ip.link("add",
        ifname="vrf-foo",
        kind="vrf",
        vrf_table=42)

► vxlan

VXLAN interfaces are like VLAN ones, but require a bit more parameters:

ip.link("add",
        ifname="vx101",
        kind="vxlan",
        vxlan_link=ip.link_lookup(ifname="eth0")[0],
        vxlan_id=101,
        vxlan_group='239.1.1.1',
        vxlan_ttl=16)

All possible vxlan parameters are listed in the module pyroute2.netlink.rtnl.ifinfmsg:... vxlan_data.

set

Set interface attributes:

# get interface index
x = ip.link_lookup(ifname="eth0")[0]
# put link down
ip.link("set", index=x, state="down")
# rename and set MAC addr
ip.link("set", index=x, address="00:11:22:33:44:55", name="bala")
# set MTU and TX queue length
ip.link("set", index=x, mtu=1000, txqlen=2000)
# bring link up
ip.link("set", index=x, state="up")

Keyword “state” is reserved. State can be “up” or “down”, it is a shortcut:

state="up":   flags=1, mask=1
state="down": flags=0, mask=0

del

Destroy the interface:

ip.link("del", index=ip.link_lookup(ifname="dummy0")[0])

dump

Dump info for all interfaces

get

Get specific interface info:

ip.link("get", index=ip.link_lookup(ifname="br0")[0])

vlan-add vlan-del

These command names are confusing and thus are deprecated. Use IPRoute.vlan_filter().

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.

neigh(command, **kwarg)¶

Neighbours operations, same as ip neigh or bridge fdb

add

Add a neighbour record, e.g.:

# add a permanent record on veth0
idx = ip.link_lookup(ifname='veth0')[0]
ip.neigh('add',
         dst='172.16.45.1',
         lladdr='00:11:22:33:44:55',
         ifindex=ip.link_lookup(ifname='veth0')[0]
         state=ndmsg.states['permanent'])

set

Set an existing record or create a new one, if it doesn’t exist.

change

Change an existing record or fail, if it doesn’t exist.

del

Delete an existing record.

dump

Dump all the records in the NDB.

route(command, **kwarg)¶

Route operations.

Keywords to set up rtmsg fields:

  • dst_len, src_len – destination and source mask(see dst below)
  • tos – type of service
  • table – routing table
  • proto – redirect, boot, static (see rt_proto)
  • scope – routing realm
  • type – unicast, local, etc. (see rt_type)

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
  • iif – incoming traffic interface
  • oif – outgoing traffic interface

etc.

One can specify mask not as dst_len, but as a part of dst, e.g.: dst=”10.0.0.0/24”.

Commands:

add

Example:

ip.route("add", dst="10.0.0.0/24", gateway="192.168.0.1")

It is possible to set also route metrics. There are two ways to do so. The first is to use ‘raw’ NLA notation:

ip.route("add",
         dst="10.0.0.0",
         mask=24,
         gateway="192.168.0.1",
         metrics={"attrs": [["RTAX_MTU", 1400],
                            ["RTAX_HOPLIMIT", 16]]})

The second way is to use shortcuts, provided by IPRouteRequest class, which is applied to **kwarg automatically:

ip.route("add",
         dst="10.0.0.0/24",
         gateway="192.168.0.1",
         metrics={"mtu": 1400,
                  "hoplimit": 16})

...

More route() examples. Blackhole route:

ip.route("add",
         dst="10.0.0.0/24",
         type="blackhole")

Multipath route:

ip.route("add",
         dst="10.0.0.0/24",
         multipath=[{"gateway": "192.168.0.1", "hops": 2},
                    {"gateway": "192.168.0.2", "hops": 1},
                    {"gateway": "192.168.0.3"}])

MPLS lwtunnel on eth0:

idx = ip.link_lookup(ifname='eth0')[0]
ip.route("add",
         dst="10.0.0.0/24",
         oif=idx,
         encap={"type": "mpls",
                "labels": "200/300"})

MPLS multipath:

idx = ip.link_lookup(ifname='eth0')[0]
ip.route("add",
         dst="10.0.0.0/24",
         table=20,
         multipath=[{"gateway": "192.168.0.1",
                     "encap": {"type": "mpls",
                               "labels": 200}},
                    {"ifindex": idx,
                     "encap": {"type": "mpls",
                               "labels": 300}}])

MPLS target can be int, string, dict or list:

"labels": 300    # simple label
"labels": "300"  # the same
"labels": (200, 300)  # stacked
"labels": "200/300"   # the same

# explicit label definition
"labels": {"bos": 1,
           "label": 300,
           "tc": 0,
           "ttl": 16}

change, replace

Commands change and replace have the same meanings, as in ip-route(8): change modifies only existing route, while replace creates a new one, if there is no such route yet.

del

Remove the route. The same syntax as for add.

get

Get route by spec.

dump

Dump all routes.

rule(command, *argv, **kwarg)¶

Rule operations

  • command — add, delete
  • table — 0 < table id < 253
  • priority — 0 < rule’s priority < 32766
  • action — type of rule, default ‘FR_ACT_NOP’ (see fibmsg.py)
  • 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
  • iifname — Input interface for Interface Based (Policy Based)
    routing’s rule
  • oifname — Output interface for Interface Based (Policy Based)
    routing’s rule

All packets route via table 10:

# 32000: from all lookup 10
# ...
ip.rule('add', table=10, priority=32000)

Default action:

# 32001: from all lookup 11 unreachable
# ...
iproute.rule('add',
             table=11,
             priority=32001,
             action='FR_ACT_UNREACHABLE')

Use source address to choose a routing table:

# 32004: from 10.64.75.141 lookup 14
# ...
iproute.rule('add',
             table=14,
             priority=32004,
             src='10.64.75.141')

Use dst address to choose a routing table:

# 32005: from 10.64.75.141/24 lookup 15
# ...
iproute.rule('add',
             table=15,
             priority=32005,
             dst='10.64.75.141',
             dst_len=24)

Match fwmark:

# 32006: from 10.64.75.141 fwmark 0xa lookup 15
# ...
iproute.rule('add',
             table=15,
             priority=32006,
             dst='10.64.75.141',
             fwmark=10)
tc(command, kind=None, index=0, 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

Target notice: if your target is a class/qdisc that applies an algorithm that can only apply to upstream traffic profile, but your keys variable explicitly references a match that is only relevant for upstream traffic, the kernel will reject the filter. Unless you’re dealing with devices like IMQs

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()
flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL
ip.tc((RTM_NEWQDISC, flags), "sfq", 1)

It should be noted that “change”, “change-class” and “change-filter” work like “replace”, “replace-class” and “replace-filter”, except they will fail if the node doesn’t exist (while it would have been created by “replace”). This is not the same behaviour as with “tc” where “change” can be used to modify the value of some options while leaving the others unchanged. However, as not all entities support this operation, we believe the “change” commands as implemented here are more useful.

Also available “modules” (returns tc plugins dict) and “help” commands:

help(ip.tc("modules")["htb"])
print(ip.tc("help", "htb"))
vlan_filter(command, **kwarg)¶

Vlan filters is another approach to support vlans in Linux. Before vlan filters were introduced, there was only one way to bridge vlans: one had to create vlan interfaces and then add them as ports:

        +------+      +----------+
net --> | eth0 | <--> | eth0.500 | <---+
        +------+      +----------+     |
                                       v
        +------+                    +-----+
net --> | eth1 |                    | br0 |
        +------+                    +-----+
                                       ^
        +------+      +----------+     |
net --> | eth2 | <--> | eth0.500 | <---+
        +------+      +----------+

It means that one has to create as many bridges, as there were vlans. Vlan filters allow to bridge together underlying interfaces and create vlans already on the bridge:

# v500 label shows which interfaces have vlan filter

        +------+ v500
net --> | eth0 | <-------+
        +------+         |
                         v
        +------+      +-----+    +---------+
net --> | eth1 | <--> | br0 |<-->| br0v500 |
        +------+      +-----+    +---------+
                         ^
        +------+ v500    |
net --> | eth2 | <-------+
        +------+

In this example vlan 500 will be allowed only on ports eth0 and eth2, though all three eth nics are bridged.

Some example code:

# create bridge
ip.link("add",
        ifname="br0",
        kind="bridge")

# attach a port
ip.link("set",
        index=ip.link_lookup(ifname="eth0")[0],
        master=ip.link_lookup(ifname="br0")[0])

# set vlan filter
ip.vlan_filter("add",
               index=ip.link_lookup(ifname="eth0")[0],
               vlan_info={"vid": 500})

# create vlan interface on the bridge
ip.link("add",
        ifname="br0v500",
        kind="vlan",
        link=ip.link_lookup(ifname="br0")[0],
        vlan_id=500)

# set all UP
ip.link("set",
        index=ip.link_lookup(ifname="br0")[0],
        state="up")
ip.link("set",
        index=ip.link_lookup(ifname="br0v500")[0],
        state="up")
ip.link("set",
        index=ip.link_lookup(ifname="eth0")[0],
        state="up")

# set IP address
ip.addr("add",
        index=ip.link_lookup(ifname="br0v500")[0],
        address="172.16.5.2",
        mask=24)

Now all the traffic to the network 172.16.5.2/24 will go
to vlan 500 only via ports that have such vlan filter.

Required arguments for vlan_filter()index and vlan_info. Vlan info struct:

{"vid": uint16,
 "flags": uint16}
More details:
  • kernel:Documentation/networking/switchdev.txt
  • pyroute2.netlink.rtnl.ifinfmsg:... vlan_info
One can specify flags as int or as a list of flag names:
  • master == 0x1
  • pvid == 0x2
  • untagged == 0x4
  • range_begin == 0x8
  • range_end == 0x10
  • brentry == 0x20

E.g.:

{"vid": 20,
 "flags": ["pvid", "untagged"]}

# is equal to
{"vid": 20,
 "flags": 6}

Commands:

add

Add vlan filter to a bridge port. Example:

ip.vlan_filter("add", index=2, vlan_info={"vid": 200})

del

Remove vlan filter from a bridge port. Example:

ip.vlan_filter("del", index=2, vlan_info={"vid": 200})
class pyroute2.iproute.RawIPRoute(fileno=None)¶

The same as IPRoute, but does not use the netlink proxy. Thus it can not manage e.g. tun/tap interfaces.

Queueing disciplines¶

drr¶

The qdisc doesn’t accept any parameters, but the class accepts quantum parameter:

ip.tc('add', 'drr', interface, '1:')
ip.tc('add-class', 'drr', interface, '1:10', quantum=1600)
ip.tc('add-class', 'drr', interface, '1:20', quantum=1600)

choke¶

Parameters:

  • limit (required) – int
  • bandwith (required) – str/int
  • min – int
  • max – int
  • avpkt – str/int, packet size
  • burst – int
  • probability – float
  • ecn – bool

Example:

ip.tc('add', 'choke', interface,
      limit=5500,
      bandwith="10mbit",
      ecn=True)

clsact¶

The clsact qdisc provides a mechanism to attach integrated filter-action classifiers to an interface, either at ingress or egress, or both. The use case shown here is using a bpf program (implemented elsewhere) to direct the packet processing. The example also uses the direct-action feature to specify what to do with each packet (pass, drop, redirect, etc.).

BPF ingress/egress example using clsact qdisc:

# open_bpf_fd is outside the scope of pyroute2
#fd = open_bpf_fd()
eth0 = ip.get_links(ifname="eth0")[0]
ip.tc("add", "clsact", eth0)
# add ingress clsact
ip.tc("add-filter", "bpf", idx, ":1", fd=fd, name="myprog",
      parent="ffff:fff2", classid=1, direct_action=True)
# add egress clsact
ip.tc("add-filter", "bpf", idx, ":1", fd=fd, name="myprog",
      parent="ffff:fff3", classid=1, direct_action=True)

hfsc¶

Simple HFSC example:

eth0 = ip.get_links(ifname="eth0")[0]
ip.tc("add", "hfsc", eth0,
      handle="1:",
      default="1:1")
ip.tc("add-class", "hfsc", eth0,
      handle="1:1",
      parent="1:0"
      rsc={"m2": "5mbit"})

HFSC curve nla types:

  • rsc: real-time curve
  • fsc: link-share curve
  • usc: upper-limit curve

htb¶

TODO: list parameters

An 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"])

Filters¶

u32¶

Filters can take an action argument, which affects the packet behavior when the filter matches. Currently the gact, bpf, and police action types are supported, and can be attached to the u32 and bpf filter types:

# An action can be a simple string, which translates to a gact type
action = "drop"

# Or it can be an explicit type (these are equivalent)
action = dict(kind="gact", action="drop")

# There can also be a chain of actions, which depend on the return
# value of the previous action.
action = [
    dict(kind="bpf", fd=fd, name=name, action="ok"),
    dict(kind="police", rate="10kbit", burst=10240, limit=0),
    dict(kind="gact", action="ok"),
]

# Add the action to a u32 match-all filter
ip.tc("add", "htb", eth0, 0x10000, default=0x200000)
ip.tc("add-filter", "u32", eth0,
      parent=0x10000,
      prio=10,
      protocol=protocols.ETH_P_ALL,
      target=0x10020,
      keys=["0x0/0x0+0"],
      action=action)

# Add two more filters: One to send packets with a src address of
# 192.168.0.1/32 into 1:10 and the second to send packets  with a
# dst address of 192.168.0.0/24 into 1:20
ip.tc("add-filter", "u32", eth0,
    parent=0x10000,
    prio=10,
    protocol=socket.AF_INET,
    target=0x10010,
    keys=["0xc0a80001/0xffffffff+12"])
    # 0xc0a800010 = 192.168.0.1
    # 0xffffffff = 255.255.255.255 (/32)
    # 12 = Source network field bit offset

ip.tc("add-filter", "u32", eth0,
    parent=0x10000,
    prio=10,
    protocol=socket.AF_INET,
    target=0x10020,
    keys=["0xc0a80000/0xffffff00+16"])
    # 0xc0a80000 = 192.168.0.0
    # 0xffffff00 = 255.255.255.0 (/24)
    # 16 = Destination network field bit offset
pyroute2-0.4.21/docs/html/mpls.html0000664000175000017500000005650113152333365017055 0ustar peetpeet00000000000000 MPLS howto — pyroute2 0.4.21 documentation

MPLS howto¶

Short introduction into Linux MPLS. Requirements:

  • kernel >= 4.4
  • modules: mpls_router, mpls_iptunnel
  • $ sudo sysctl net.mpls.platform_labels=$x, where $x – number of labels
  • pyroute2 >= 0.4.0

MPLS labels¶

Possible label formats:

# int
"dst": 20

# list of ints
"newdst": [20]
"newdst": [20, 30]

# string
"labels": "20/30"

Any of these notations should be accepted by pyroute2, if not – try another format and submit an issue to the project github page. The code is quite new, some issues are possible.

Refer also to the test cases, there are many usage samples:

  • tests/general/test_ipr.py
  • tests/general/test_ipdb.py

IPRoute¶

MPLS routes¶

Label swap:

from pyroute2 import IPRoute
from pyroute2.common import AF_MPLS

ipr = IPRoute()
# get the `eth0` interface's index:
idx = ipr.link_lookup(ifname="eth0")[0]
# create the request
req = {"family": AF_MPLS,
       "oif": idx,
       "dst": 20,
       "newdst": [30]}
# set up the route
ipr.route("add", **req)

Notice, that dst is a single label, while newdst is a stack. Label push:

req = {"family": AF_MPLS,
       "oif": idx,
       "dst": 20,
       "newdst": [20, 30]}
ipr.route("add", **req)

One can set up also the via field:

from socket import AF_INET

req = {"family": AF_MPLS,
       "oif": idx,
       "dst": 20,
       "newdst": [30],
       "via": {"family": AF_INET,
               "addr": "1.2.3.4"}}
ipr.route("add", **req)

MPLS lwtunnel¶

To inject IP packets into MPLS:

req = {"dst": "1.2.3.0/24",
       "oif": idx,
       "encap": {"type": "mpls",
                 "labels": [202, 303]}}
ipr.route("add", **req)

IPDB¶

MPLS routes¶

The IPDB database also supports MPLS routes, they are reflected in the ipdb.routes.tables[“mpls”]:

>>> (ipdb
...  .routes
...  .add({"family": AF_MPLS,
...        "oif": ipdb.interfaces["eth0"]["index"],
...        "dst": 20,
...        "newdst": [30]})
...  .commit())
<skip>
>>> (ipdb
...  .routes
...  .add({"family": AF_MPLS,
...        "oif": ipdb.interfaces["eth0"]["index"],
...        "dst": 22,
...        "newdst": [22, 42]})
...  .commit())
<skip>
>>> ipdb.routes.tables["mpls"].keys()
[20, 22]

Pls notice, that there is only one MPLS routing table.

Multipath MPLS:

with IDPB() as ipdb:
    (ipdb
     .routes
     .add({"family": AF_MPLS,
           "dst": 20,
           "multipath": [{"via": {"family": AF_INET,
                                  "addr": "10.0.0.2"},
                          "oif": ipdb.interfaces["eth0"]["index"],
                          "newdst": [30]},
                         {"via": {"family": AF_INET,
                                  "addr": "10.0.0.3"},
                          "oif": ipdb.interfaces["eth0"]["index"],
                          "newdst": [40]}]})
     .commit())

MPLS lwtunnel¶

LWtunnel routes reside in common route tables:

with IPDB() as ipdb:
    (ipdb
     .routes
     .add({"dst": "1.2.3.0/24",
           "oif": ipdb.interfaces["eth0"]["index"],
           "encap": {"type": "mpls",
                     "labels": [22]}})
     .commit())
    print(ipdb.routes["1.2.3.0/24"])

Multipath MPLS lwtunnel:

with IPDB() as ipdb:
    (ipdb
     .routes
     .add({"dst": "1.2.3.0/24",
           "table": 200,
           "multipath": [{"oif": ipdb.interfaces["eth0"]["index"],
                          "gateway": "10.0.0.2",
                          "encap": {"type": "mpls",
                                    "labels": [200, 300]}},
                         {"oif": ipdb.interfaces["eth1"]["index"],
                          "gateway": "172.16.0.2",
                          "encap": {"type": "mpls",
                                    "labels": [200, 300]}}]})
     .commit())
    print(ipdb.routes.tables[200]["1.2.3.0/24"])
pyroute2-0.4.21/docs/html/dhcp.html0000664000175000017500000003712413152333365017020 0ustar peetpeet00000000000000 DHCP support — pyroute2 0.4.21 documentation

DHCP support¶

DHCP support in pyroute2 is in very initial state, so it is in the «Development section» yet. DHCP protocol has nothing to do with netlink, but pyroute2 slowly moving from netlink-only library to some more general networking framework.

DHCP protocol¶

The DHCP implementation here is far from complete, but already provides some basic functionality. Later it will be extended with IPv6 support and more DHCP options will be added.

Right now it can be interesting mostly to developers, but not users and/or system administrators. So, the development hints first.

The packet structure description is intentionally implemented as for netlink packets. Later these two parsers, netlink and generic, can be merged, so the syntax is more or less compatible.

Packet fields¶

There are two big groups of items within any DHCP packet. First, there are BOOTP/DHCP packet fields, they’re defined with the fields attribute:

class dhcp4msg(msg):
    fields = ((name, format, policy),
              (name, format, policy),
              ...
              (name, format, policy))

The name can be any literal. Format should be specified as for the struct module, like B for uint8, or i for int32, or >Q for big-endian uint64. There are also aliases defined, so one can write uint8 or be16, or like that. Possible aliases can be seen in the pyroute2.protocols module.

The policy is a bit complicated. It can be a number or literal, and it will mean that it is a default value, that should be encoded if no other value is given.

But when the policy is a dictionary, it can contain keys as follows:

'l2addr': {'format': '6B',
           'decode': ...,
           'encode': ...}

Keys encode and decode should contain filters to be used in decoding and encoding procedures. The encoding filter should accept the value from user’s definition and should return a value that can be packed using format. The decoding filter should accept a value, decoded according to format, and should return value that can be used by a user.

The struct module can not decode IP addresses etc, so they should be decoded as 4s, e.g. Further transformation from 4 bytes string to a string like ‘10.0.0.1’ performs the filter.

DHCP options¶

DHCP options are described in a similar way:

options = ((code, name, format),
           (code, name, format),
           ...
           (code, name, format))

Code is a uint8 value, name can be any string literal. Format is a string, that must have a corresponding class, inherited from pyroute2.dhcp.option. One can find these classes in pyroute2.dhcp (more generic) or in pyroute2.dhcp.dhcp4msg (IPv4-specific). The option class must reside within dhcp message class.

Every option class can be decoded in two ways. If it has fixed width fields, it can be decoded with ordinary msg routines, and in this case it can look like that:

class client_id(option):
    fields = (('type', 'uint8'),
              ('key', 'l2addr'))

If it must be decoded by some custom rules, one can define the policy just like for the fields above:

class array8(option):
    policy = {'format': 'string',
              'encode': lambda x: array('B', x).tobytes(),
              'decode': lambda x: array('B', x).tolist()}

In the corresponding modules, like in pyroute2.dhcp.dhcp4msg, one can define as many custom DHCP options, as one need. Just be sure, that they are compatible with the DHCP server and all fit into 1..254 (uint8) – the 0 code is used for padding and the code 255 is the end of options code.

IPv4 DHCP socket¶

class pyroute2.dhcp.dhcp4socket.DHCP4Socket(ifname, port=68)¶

Parameters:

  • ifname – interface name to work on

This raw socket binds to an interface and installs BPF filter to get only its UDP port. It can be used in poll/select and provides also the context manager protocol, so can be used in with statements.

It does not provide any DHCP state machine, and does not inspect DHCP packets, it is totally up to you. No default values are provided here, except xid – DHCP transaction ID. If xid is not provided, DHCP4Socket generates it for outgoing messages.

get()¶

Get the next incoming packet from the socket and try to decode it as IPv4 DHCP. No analysis is done here, only MAC/IPv4/UDP headers are stripped out, and the rest is interpreted as DHCP.

put(msg=None, dport=67)¶

Put DHCP message. Parameters:

  • msg – dhcp4msg instance
  • dport – DHCP server port

If msg is not provided, it is constructed as default BOOTREQUEST + DHCPDISCOVER.

Examples:

sock.put(dhcp4msg({'op': BOOTREQUEST,
                   'chaddr': 'ff:11:22:33:44:55',
                   'options': {'message_type': DHCPREQUEST,
                               'parameter_list': [1, 3, 6, 12, 15],
                               'requested_ip': '172.16.101.2',
                               'server_id': '172.16.101.1'}}))

The method returns dhcp4msg that was sent, so one can get from there xid (transaction id) and other details.

pyroute2-0.4.21/docs/html/index.html0000664000175000017500000003604113152333365017206 0ustar peetpeet00000000000000 pyroute2 netlink library — pyroute2 0.4.21 documentation
pyroute2-0.4.21/docs/html/changelog.html0000664000175000017500000007024713152333365020034 0ustar peetpeet00000000000000 changelog — pyroute2 0.4.21 documentation

changelog¶

  • 0.4.21
    • ipdb: #416 – workaround for the vrf_table issue
  • 0.4.20
    • ipdb: #392 – fix MPLS route key reference
    • ipdb: #408 – fix IPv6 routes in tables >= 256
    • ipdb: use tos as a route key
  • 0.4.19
    • ipdb: global methods review(), drop(), dump(), load()
    • ipdb: support VLAN protocol (802.1q, 802.1ad)
    • ipdb: initial support for IPv6 rules
    • ipdb: #388 – normalize IPv6 addresses
    • ipdb: #391 – support both IPv4 and IPv6 default routes
    • ipdb: #394 – correctly work with route priorities
    • netns: non-destructive netns.create()
  • 0.4.18
    • ipdb: #379 [critical] – routes in global commits
    • ipdb: #380 – global commit with disabled plugins
    • ipdb: #381 – exceptions fixed
    • ipdb: #382 – manage dependent routes during interface commits
    • ipdb: #384 – global review()
    • ipdb: #385 – global drop()
    • netns: #383 – support ppc64
    • general: public API refactored (same signatures; to be documented)
  • 0.4.17
    • req: #374 [critical] – mode nla init
    • iproute: #378 [critical] – fix flush_routes() to respect filters
    • ifinfmsg: #376 – fix data plugins API to support pyinstaller
  • 0.4.16
    • ipdb: race fixed: remove port/bridge
    • ipdb: #280 – race fixed: port/bridge
    • ipdb: #302 – ipaddr views: [ifname].ipaddr.ipv4, [ifname]ipaddr.ipv6
    • ipdb: #357 – allow bridge timings to have some delta
    • ipdb: #338 – allow to fix interface objects from failed create()
    • rtnl: #336 – fix vlan flags
    • iproute: #342 – the match method takes any callable
    • nlsocket: #367 – increase default SO_SNDBUF
    • ifinfmsg: support tuntap on armv6l, armv7l platforms
  • 0.4.15
    • req: #365 – full and short nla notation fixed, critical
    • iproute: #364 – new method, brport()
    • ipdb: – support bridge port options
  • 0.4.14
    • event: new genl protocols set: VFS_DQUOT, acpi_event, thermal_event
    • ipdb: #310 – fixed priority change on routes
    • ipdb: #349 – fix setting ifalias on interfaces
    • ipdb: #353 – mitigate kernel oops during bridge creation
    • ipdb: #354 – allow to explicitly choose plugins to load
    • ipdb: #359 – provide read-only context managers
    • rtnl: #336 – vlan flags support
    • rtnl: #352 – support interface type plugins
    • tc: #344 – mirred action
    • tc: #346 – connmark action
    • netlink: #358 – memory optimization
    • config: #360 – generic asyncio config
    • iproute: #362 – allow to change or replace a qdisc
  • 0.4.13
    • ipset: full rework of the IPSET_ATTR_DATA and IPSET_ATTR_ADT ACHTUNG: this commit may break API compatibility
    • ipset: hash:mac support
    • ipset: list:set support
    • ipdb: throw EEXIST when creates VLAN/VXLAN devs with same ID, but under different names
    • tests: #329 – include unit tests into the bundle
    • legal: E/// logo removed
  • 0.4.12
    • ipdb: #314 – let users choose RTNL groups IPDB listens to
    • ipdb: #321 – isolate net_ns_.* setup in a separate code block
    • ipdb: #322 – IPv6 updates on interfaces in DOWN state
    • ifinfmsg: allow absolute/relative paths in the net_ns_fd NLA
    • ipset: #323 – support setting counters on ipset add
    • ipset: headers() command
    • ipset: revisions
    • ipset: #326 – mark types
  • 0.4.11
    • rtnl: #284 – support vlan_flags
    • ipdb: #288 – do not inore link-local addresses
    • ipdb: #300 – sort ip addresses
    • ipdb: #306 – support net_ns_pid
    • ipdb: #307 – fix IPv6 routes management
    • ipdb: #311 – vlan interfaces address loading
    • iprsocket: #305 – support NETLINK_LISTEN_ALL_NSID
  • 0.4.10
    • devlink: fix fd leak on broken init
  • 0.4.9
    • sock_diag: initial NETLINK_SOCK_DIAG support
    • rtnl: fix critical fd leak in the compat code
  • 0.4.8
    • rtnl: compat proxying fix
  • 0.4.7
    • rtnl: compat code is back
    • netns: custom netns path support
    • ipset: multiple improvements
  • 0.4.6
    • ipdb: #278 – fix initial ports mapping
    • ipset: #277 – fix ADT attributes parsing
    • nl80211: #274, #275, #276 – BSS-related fixes
  • 0.4.5
    • ifinfmsg: GTP interfaces support
    • generic: devlink protocol support
    • generic: code cleanup
  • 0.4.4
    • iproute: #262 – get_vlans() fix
    • iproute: default mask 32 for IPv4 in addr()
    • rtmsg: #260 – RTA_FLOW support
  • 0.4.3
    • ipdb: #259 – critical Interface class fix
    • benchmark: initial release
  • 0.4.2
    • ipdb: event modules
    • ipdb: on-demand views
    • ipdb: rules management
    • ipdb: bridge controls
    • ipdb: #258 – important Python compatibility fixes
    • netns: #257 – pipe leak fix
    • netlink: support pickling for nlmsg
  • 0.4.1
    • netlink: no buffer copying in the parser
    • netlink: parse NLA on demand
    • ipdb: #244 – lwtunnel multipath fixes
    • iproute: #235 – route types
    • docs updated
  • 0.4.0
    • ACHTUNG: old kernels compatibility code is dropped
    • ACHTUNG: IPDB uses two separate sockets for monitoring and commands
    • ipdb: #244 – multipath lwtunnel
    • ipdb: #242 – AF_MPLS routes
    • ipdb: #241, #234 – fix create(..., reuse=True)
    • ipdb: #239 – route encap and metrics fixed
    • ipdb: #238 – generic port management
    • ipdb: #235 – support route scope and type
    • ipdb: #230, #232 – routes GC (work in progress)
    • rtnl: #245 – do not fail if /proc/net/psched doesn’t exist
    • rtnl: #233 – support VRF interfaces (requires net-next)
  • 0.3.21
    • ipdb: #231 – return ipdb.common as deprecated
  • 0.3.20
    • iproute: vlan_filter()
    • iproute: #229 – FDB management
    • general: exceptions re-exported via the root module
  • 0.3.19
    • rtmsg: #227 – MPLS lwtunnel basic support
    • iproute: route() docs updated
    • general: #228 – exceptions layout changed
    • package-rh: rpm subpackages
  • 0.3.18
    • version bump – include docs in the release tarball
  • 0.3.17
    • tcmsg: qdiscs and filters as plugins
    • tcmsg: #223 – tc clsact and bpf direct-action
    • tcmsg: plug, codel, choke, drr qdiscs
    • tests: CI in VMs (see civm project)
    • tests: xunit output
    • ifinfmsg: tuntap support in i386, i686
    • ifinfmsg: #207 – support vlan filters
    • examples: #226 – included in the release tarball
    • ipdb: partial commits, initial support
  • 0.3.16
    • ipdb: fix the multiple IPs in one commit case
    • rtnl: support veth peer attributes
    • netns: support 32bit i686
    • netns: fix MIPS support
    • netns: fix tun/tap creation
    • netns: fix interface move between namespaces
    • tcmsg: support hfsc, fq_codel, codel qdiscs
    • nftables: initial support
    • netlink: dump/load messages to/from simple types
  • 0.3.15
    • netns: #194 – fix fd leak
    • iproute: #184 – fix routes dump
    • rtnl: TCA_ACT_BPF support
    • rtnl: ipvlan support
    • rtnl: OVS support removed
    • iproute: rule() improved to support all NLAs
    • project supported by Ericsson
  • 0.3.14
    • package-rh: spec fixed
    • package-rh: both licenses added
    • remote: fixed the setup.py record
  • 0.3.13
    • package-rh: new rpm for Fedora and CentOS
    • remote: new draft of the remote protocol
    • netns: refactored using the new remote protocol
    • ipdb: gretap support
  • 0.3.12
    • ipdb: new Interface.wait_ip() routine
    • ipdb: #175 – fix master attribute cleanup
    • ipdb: #171 – support multipath routes
    • ipdb: memory consumption improvements
    • rtmsg: MPLS support
    • rtmsg: RTA_VIA support
    • iwutil: #174 – fix FREQ_FIXED flag
  • 0.3.11
    • ipdb: #161 – fix memory allocations
    • nlsocket: #161 – remove monitor mode
  • 0.3.10
    • rtnl: added BPF filters
    • rtnl: LWtunnel support in ifinfmsg
    • ipdb: support address attributes
    • ipdb: global transactions, initial version
    • ipdb: routes refactored to use key index (speed up)
    • config: eventlet support embedded (thanks to Angus Lees)
    • iproute: replace tc classes
    • iproute: flush_addr(), flush_rules()
    • iproute: rule() refactored
    • netns: proxy file objects (stdin, stdout, stderr)
  • 0.3.9
    • root imports: #109, #135 – issubclass, isinstance
    • iwutil: multiple improvements
    • iwutil: initial tests
    • proxy: correctly forward NetlinkError
    • iproute: neighbour tables support
    • iproute: #147, filters on dump calls
    • config: initial usage of capabilities
  • 0.3.8
    • docs: inheritance diagrams
    • nlsocket: #126, #132 – resource deallocation
    • arch: #128, #131 – MIPS support
    • setup.py: #133 – syntax error during install on Python2
  • 0.3.7
    • ipdb: new routing syntax
    • ipdb: sync interface movement between namespaces
    • ipdb: #125 – fix route metrics
    • netns: new class NSPopen
    • netns: #119 – i386 syscall
    • netns: #122 – return correct errno
    • netlink: #126 – fix socket reuse
  • 0.3.6
    • dhcp: initial release DHCPv4
    • license: dual GPLv2+ and Apache v2.0
    • ovs: port add/delete
    • macvlan, macvtap: basic support
    • vxlan: basic support
    • ipset: basic support
  • 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.4.21/docs/html/genindex.html0000664000175000017500000004265213152333365017705 0ustar peetpeet00000000000000 Index — pyroute2 0.4.21 documentation

Index

A | B | C | D | F | G | H | I | L | M | N | P | R | S | T | U | V

A

B

C

D

F

G

H

I

L

M

N

P

R

S

T

U

V

pyroute2-0.4.21/docs/html/ipdb.html0000664000175000017500000021772113152333365017023 0ustar peetpeet00000000000000 IPDB module — pyroute2 0.4.21 documentation

IPDB module¶

Basically, IPDB is a transactional database, containing records, that represent network stack objects. Any change in the database is not reflected immediately in OS, 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, just forwards requests to the kernel, and doesn’t wait for the system state. So it’s up to developer to check, whether the requested object is really set up or not.

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. But unlike IPRoute, the IPDB ensures the changes to be reflected in the system:

with IPDB() as ipdb:
    with ipdb.interfaces['eth0'] as i:
        i.up()
        i.add_ip('192.168.0.2/24')
        i.add_ip('192.168.0.3/24')
    # ---> <--- here you can expect `eth0` is up
    #           and has these two addresses, so
    #           the following code can rely on that

So IPDB is updated asynchronously, but the commit() operation is synchronous. At least, it is planned to be such.

NB: In the example above `commit()` is implied with the `__exit__()` of the `with` statement.

The choice between IPDB and IPRoute 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 the 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.

IPDB and other software¶

IPDB is designed to be a non-exclusive network settings database. There may be several IPDB instances on the same OS, as well as other network management software, such as NetworkManager etc.

The IPDB transactions should not interfere with other software settings, unless they touch the same objects. E.g., if IPDB brings an interface up, while NM shuts it down, there will be a race condition.

An example:

# IPDB code                       #  NetworkManager at the same time:
ipdb.interfaces['eth0'].up()      #
ipdb.interfaces['eth0'].commit()  #  $ sudo nmcli con down eth0
# ---> <---
# The eth0 state here is undefined. Some of the commands
# above will fail

But as long as the software doesn’t touch the same objects, there will be no conflicts. Another example:

# IPDB code                         # At the same time, NetworkManager
with ipdb.interfaces['eth0'] as i:  # adds addresses:
    i.add_ip('172.16.254.2/24')     #  * 10.0.0.2/24
    i.add_ip('172.16.254.3/24')     #  * 10.0.0.3/24
# ---> <---
# At this point the eth0 interface will have all four addresses.
# If the IPDB transaction fails by some reason, only IPDB addresses
# will be rolled back.

There may be a need to prevent other software from changing the network settings. There is no locking at the kernel level, but IPDB can revert all the changes as soon as they appear on the interface:

# IPDB code
ipdb.interfaces['eth0'].freeze()
                                   # Here some other software tries to
                                   # add an address, or to remove the old
                                   # one
# ---> <---
# At this point the eth0 interface will have all the same settings as
# at the `freeze()` call moment. Newly added addresses will be removed,
# all the deleted addresses will be restored.
#
# Please notice, that an address removal may cause also a routes removal,
# and that is the thing that IPDB can not neither prevent, nor revert.

ipdb.interfaces['eth0'].unfreeze()

Quickstart¶

Simple tutorial:

from pyroute2 import IPDB
# several IPDB instances are supported within on process
ipdb = IPDB()

# commit is called automatically upon the exit from `with`
# statement
with ipdb.interfaces.eth0 as i:
    i.address = '00:11:22:33:44:55'
    i.ifname = 'bala'
    i.txqlen = 2000

# basic routing support
ipdb.routes.add({'dst': 'default',
                 'gateway': '10.0.0.1'}).commit()

# do not forget to shutdown IPDB
ipdb.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 supports functional-like syntax also:

from pyroute2 import IPDB
with IPDB() as ipdb:
    intf = (ipdb.interfaces['eth0']
            .add_ip('10.0.0.2/24')
            .add_ip('10.0.0.3/24')
            .set_address('00:11:22:33:44:55')
            .set_mtu(1460)
            .set_name('external')
            .commit())
    # ---> <--- here you have the interface reference with
    #           all the changes applied: renamed, added ipaddr,
    #           changed macaddr and mtu.
    ...  # some code

# pls notice, that the interface reference will not work
# outside of `with IPDB() ...`

Transaction modes¶

IPDB has several operating modes:

  • ‘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

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 current_tx transaction, and commit() or drop() it. Also, multiple 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:

ipdb.interfaces['tap0'].down()
ipdb.interfaces['tap0']['address'] = '00:11:22:33:44:55'
...

Context managers¶

Transactional objects (interfaces, routes) can act as context managers in the same way as IPDB does itself:

with ipdb.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.

Read-only interface views¶

Using an interface as a context manager will start a transaction. Sometimes it is not what one needs. To avoid unnecessary transactions, and to avoid the risk to occasionally change interface attributes, one can use read-only views:

with ipdb.interfaces[1].ro as iface:
    print(iface.ifname)
    print(iface.address)

The .ro view neither starts transactions, nor allows to change anything, raising the RuntimeError exception.

The same read-only views are available for routes and rules.

Create interfaces¶

IPDB can also create virtual interfaces:

with ipdb.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 IPDB.create() call has the same syntax as IPRoute.link(‘add’, ...), except you shouldn’t specify the ‘add’ command. Refer to IPRoute docs for details.

Please notice, that the interface object stays in the database even if there was an error during the interface creation. It is done so to make it possible to fix the interface object and try to run commit() again. Or you can drop the interface object with the .remove().commit() call.

IP address management¶

IP addresses on interfaces may be managed using add_ip() and del_ip():

with ipdb.interfaces['eth0'] as eth:
    eth.add_ip('10.0.0.1/24')
    eth.add_ip('10.0.0.2/24')
    eth.add_ip('2001:4c8:1023:108::39/64')
    eth.del_ip('172.16.12.5/24')

The address format may be either a string with ‘address/mask’ notation, or a pair of ‘address’, mask:

with ipdb.interfaces['eth0'] as eth:
    eth.add_ip('10.0.0.1', 24)
    eth.del_ip('172.16.12.5', 24)

The ipaddr attribute contains all the IP addresses of the interface, which are acessible in different ways. Getting an iterator from ipaddr gives you a sequence of tuples (‘address’, mask):

>>> for addr in ipdb.interfaces['eth0'].ipaddr:
...    print(ipaddr)
...
('10.0.0.2', 24)
('10.0.0.1', 24)

Getting one IP from ipaddr returns a dict object with full spec:

>>> ipdb.interfaces['eth0'].ipaddr[0]:
    {'family': 2,
     'broadcast': None,
     'flags': 128,
     'address': '10.0.0.2',
     'prefixlen': 24,
     'local': '10.0.0.2'}
>>> ipdb.intefaces['eth0'].ipaddr['10.0.0.2/24']:
    {'family': 2,
     'broadcast': None,
     'flags': 128,
     'address': '10.0.0.2',
     'prefixlen': 24,
     'local': '10.0.0.2'}

The API is a bit weird, but it’s because of historical reasons. In the future it may be changed.

Another feature of the ipaddr attribute is views:

>>> ipdb.interfaces['eth0'].ipaddr.ipv4:
    (('10.0.0.2', 24), ('10.0.0.1', 24))
>>> ipdb.interfaces['eth0'].ipaddr.ipv6:
    (('2001:4c8:1023:108::39', 64),)

The views, as well as the ipaddr attribute itself are not supposed to be changed by user, but only by the internal API.

Bridge interfaces¶

Modern kernels provide possibility to manage bridge interface properties such as STP, forward delay, ageing time etc. Names of these properties start with br_, like br_ageing_time, br_forward_delay e.g.:

[x for x in dir(ipdb.interfaces.virbr0) if x.startswith('br_')]

Bridge ports¶

IPDB supports specific bridge port parameters, such as proxyarp, unicast/multicast flood, cost etc.:

with ipdb.interfaces['br-port0'] as p:
    p.brport_cost = 200
    p.brport_unicast_flood = 0
    p.brport_proxyarp = 0

Ports management¶

IPDB provides a uniform API to manage bridge, bond and vrf ports:

with ipdb.interfaces['br-int'] as br:
    br.add_port('veth0')
    br.add_port(ipdb.interfaces.veth1)
    br.add_port(700)
    br.del_port('veth2')

Both add_port() and del_port() accept three types of arguments:

  • ‘veth0’ – interface name as a string
  • ipdb.interfaces.veth1 – IPDB interface object
  • 700 – interface index, an integer

Routes management¶

IPDB has a simple yet useful routing management interface.

Create a route¶

To add a route, there is an easy to use 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
ipdb.routes.add(spec).commit()

# pass spec as kwargs
ipdb.routes.add(**spec).commit()

# use keyword arguments explicitly
ipdb.routes.add(dst='172.16.1.0/24', oif=4, ...).commit()

Please notice, that the device can be specified with oif (output interface) or iif (input interface), the device keyword is not supported anymore.

More examples:

# specify table and priority
(ipdb.routes
 .add(dst='172.16.1.0/24',
      gateway='192.168.0.1',
      table=100,
      priority=10)
 .commit())

The priority field is what the iproute2 utility calls metric – see also below.

Get a route¶

To access and change the routes, one can use notations as follows:

# default table (254)
#
# change the route gateway and mtu
#
with ipdb.routes['172.16.1.0/24'] as route:
    route.gateway = '192.168.122.60'
    route.metrics.mtu = 1500

# access the default route
print(ipdb.routes['default'])

# change the default gateway
with ipdb.routes['default'] as route:
    route.gateway = '10.0.0.1'

By default, the path ipdb.routes reflects only the main routing table (254). But Linux supports much more routing tables, so does IPDB:

In [1]: ipdb.routes.tables.keys()
Out[1]: [0, 254, 255]

In [2]: len(ipdb.routes.tables[255])
Out[2]: 11  # => 11 automatic routes in the table local

It is important to understand, that routing tables keys in IPDB are not only the destination prefix. The key consists of ‘prefix/mask’ string and the route priority (if any):

In [1]: ipdb.routes.tables[254].idx.keys()
Out[1]:
[RouteKey(dst='default', scope=0, table=254, family=2, ...),
 RouteKey(dst='172.17.0.0/16', scope=253, table=254, ...),
 RouteKey(dst='172.16.254.0/24', scope=253, table=254, ...),
 RouteKey(dst='192.168.122.0/24', scope=253, table=254, ...),
 RouteKey(dst='fe80::/64', scope=0, table=254, family=10, ...)]

But a routing table in IPDB allows several variants of the route spec. The simplest case is to retrieve a route by prefix, if there is only one match:

# get route by prefix
ipdb.routes['172.16.1.0/24']

# get route by a special name
ipdb.routes['default']

If there are more than one route that matches the spec, only the first one will be retrieved. One should iterate all the records and filter by a key to retrieve all matches:

# only one route will be retrieved
ipdb.routes['fe80::/64']

# get all routes by this prefix
[ x for x in ipdb.routes if x['dst'] == 'fe80::/64' ]

It is also possible to use dicts as specs:

# get IPv4 default route
ipdb.routes[{'dst': 'default', 'family': AF_INET}]

# get IPv6 default route
ipdb.routes[{'dst': 'default', 'family': AF_INET6}]

# get route by priority
ipdb.routes.table[100][{'dst': '10.0.0.0/24', 'priority': 10}]

While this notation returns one route, there is a method to get all the routes matching the spec:

# get all the routes from all the tables via some interface
ipdb.routes.filter({'oif': idx})

# get all IPv6 routes from some table
ipdb.routes.table[tnum].filter({'family': AF_INET6})

Route metrics¶

A special object is dedicated to route metrics, one can access it via route.metrics or route[‘metrics’]:

# these two statements are equal:
with ipdb.routes['172.16.1.0/24'] as route:
    route['metrics']['mtu'] = 1400

with ipdb.routes['172.16.1.0/24'] as route:
    route.metrics.mtu = 1400

Possible metrics are defined in rtmsg.py:rtmsg.metrics, e.g. RTAX_HOPLIMIT means hoplimit metric etc.

Multipath routing¶

Multipath nexthops are managed via route.add_nh() and route.del_nh() methods. They are available to review via route.multipath, but one should not directly add/remove/modify nexthops in route.multipath, as the changes will not be committed correctly.

To create a multipath route:

ipdb.routes.add({'dst': '172.16.232.0/24',
                 'multipath': [{'gateway': '172.16.231.2',
                                'hops': 2},
                               {'gateway': '172.16.231.3',
                                'hops': 1},
                               {'gateway': '172.16.231.4'}]}).commit()

To change a multipath route:

with ipdb.routes['172.16.232.0/24'] as r:
    r.add_nh({'gateway': '172.16.231.5'})
    r.del_nh({'gateway': '172.16.231.4'})

Another possible way is to create a normal route and turn it into multipath by add_nh():

# create a non-MP route with one gateway:
(ipdb
 .routes
 .add({'dst': '172.16.232.0/24',
       'gateway': '172.16.231.2'})
 .commit())

# turn it to become a MP route:
(ipdb
 .routes['172.16.232.0/24']
 .add_nh({'gateway': '172.16.231.3'})
 .commit())

# here the route will contain two NH records, with
# gateways 172.16.231.2 and 172.16.231.3

# remove one NH and turn the route to be a normal one
(ipdb
 .routes['172.16.232.0/24']
 .del_nh({'gateway': '172.16.231.2'})
 .commit())

# thereafter the traffic to 172.16.232.0/24 will go only
# via 172.16.231.3

Differences from the iproute2 syntax¶

By historical reasons, iproute2 uses names that differs from what the kernel uses. E.g., iproute2 uses weight for multipath route hops instead of hops, where weight == (hops + 1). Thus, a route created with hops == 2 will be listed by iproute2 as weight 3.

Another significant difference is metrics. The pyroute2 library uses the kernel naming scheme, where metrics means mtu, rtt, window etc. The iproute2 utility uses metric (not metrics) as a name for the priority field.

In examples:

# -------------------------------------------------------
# iproute2 command:
$ ip route add default \
    nexthop via 172.16.0.1 weight 2 \
    nexthop via 172.16.0.2 weight 9

# pyroute2 code:
(ipdb
 .routes
 .add({'dst': 'default',
       'multipath': [{'gateway': '172.16.0.1', 'hops': 1},
                     {'gateway': '172.16.0.2', 'hops': 8}])
 .commit())

# -------------------------------------------------------
# iproute2 command:
$ ip route add default via 172.16.0.2 metric 200

# pyroute2 code:
(ipdb
 .routes
 .add({'dst': 'default',
       'gateway': '172.16.0.2',
       'priority': 200})
 .commit())

# -------------------------------------------------------
# iproute2 command:
$ ip route add default via 172.16.0.2 mtu 1460

# pyroute2 code:
(ipdb
 .routes
 .add({'dst': 'default',
       'gateway': '172.16.0.2',
       'metrics': {'mtu': 1460}})
 .commit())

Multipath default routes¶

Warning

As of the merge of kill_rtcache into the kernel, and it’s release in ~3.6, weighted default routes no longer work in Linux.

Please refer to https://github.com/svinota/pyroute2/issues/171#issuecomment-149297244 for details.

Rules management¶

IPDB provides a basic IP rules management system.

Create a rule¶

Syntax is almost the same as for routes:

# rule spec
spec = {'src': '172.16.1.0/24',
        'table': 200,
        'priority': 15000}

ipdb.rules.add(spec).commit()

Get a rule¶

The way IPDB handles IP rules is almost the same as routes, but rule keys are more complicated – the Linux kernel doesn’t use keys for rules, but instead iterates all the records until the first one w/o any attribute mismatch.

The fields that the kernel uses to compare rules, IPDB uses as the key fields (see pyroute2/ipdb/rule.py:RuleKey)

There are also more ways to find a record, as with routes:

# 1. iterate all the records
for record in ipdb.rules:
    match(record)

# 2. an integer as the key matches the first
#    rule with that priority
ipdb.rules[32565]

# 3. a dict as the key returns the first match
#    for all the specified attrs
ipdb.rules[{'dst': '10.0.0.0/24', 'table': 200}]

Priorities¶

Thus, the rule priority is not a key, neither in the kernel, nor in IPDB. One should not rely on priorities as on keys, there may be several rules with the same priority, and it often happens, e.g. on Android systems.

Persistence¶

There is no change operation for the rule records in the kernel, so only add/del work. When IPDB changes a record, it effectively deletes the old one and creates the new with new parameters, but the object, referring the record, stays the same. Also that means, that IPDB can not recognize the situation, when someone else does the same. So if there is another program changing records by del/add operations, even another IPDB instance, referring objects in the IPDB will be recreated.

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.

The class API¶

class pyroute2.ipdb.main.IPDB(nl=None, mode='implicit', restart_on_error=None, nl_async=None, nl_bind_groups=67372509, ignore_rtables=None, callbacks=None, sort_addresses=False, plugins=None)¶

The class that maintains information about network setup of the host. Monitoring netlink events allows it to react immediately. It uses no polling.

register_callback(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:
  • callback function
  • mode (optional, default=”post”)

The callback should be a routine, that accepts three arguments:

cb(ipdb, msg, action)

Arguments are:

  • ipdb is a reference to IPDB instance, that invokes
    the callback.
  • msg is a message arrived
  • 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]
release(complete=True)¶

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.

pyroute2-0.4.21/docs/html/.buildinfo0000664000175000017500000000034613152333366017165 0ustar peetpeet00000000000000# Sphinx build info version 1 # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. config: 38885f03520045a9cfcb1b8f80b2d988 tags: 645f666f9bcd5a90fca523b33c5a78b7 pyroute2-0.4.21/docs/html/py-modindex.html0000664000175000017500000001660713152333365020342 0ustar peetpeet00000000000000 Python Module Index — pyroute2 0.4.21 documentation pyroute2-0.4.21/docs/html/search.html0000664000175000017500000000725213152333365017346 0ustar peetpeet00000000000000 Search — pyroute2 0.4.21 documentation

Search

Please activate JavaScript to enable the search functionality.

From here you can search these documents. Enter your search words into the box below and click "search". Note that the search function will automatically search for all of the words. Pages containing fewer words won't appear in the result list.

pyroute2-0.4.21/PKG-INFO0000664000175000017500000002144513152333366014415 0ustar peetpeet00000000000000Metadata-Version: 1.1 Name: pyroute2 Version: 0.4.21 Summary: Python Netlink library Home-page: https://github.com/svinota/pyroute2 Author: Peter V. Saveliev Author-email: peter@svinota.eu License: dual license GPLv2+ and Apache v2 Description: pyroute2 ======== Pyroute2 is a pure Python **netlink** library. It requires only Python stdlib, no 3rd party libraries. The library was started as an RTNL protocol implementation, so the name is **pyroute2**, but now it supports many netlink protocols. Some supported netlink families and protocols: * **rtnl**, network settings --- addresses, routes, traffic controls * **nfnetlink** --- netfilter API: **ipset**, **nftables**, ... * **ipq** --- simplest userspace packet filtering, iptables QUEUE target * **devlink** --- manage and monitor devlink-enabled hardware * **generic** --- generic netlink families * **nl80211** --- wireless functions API (basic support) * **taskstats** --- extended process statistics * **acpi_events** --- ACPI events monitoring * **thermal_events** --- thermal events monitoring * **VFS_DQUOT** --- disk quota events monitoring the simplest usecase -------------------- The socket objects, provided by the library, are actual socket objects with a little bit extended API. The additional functionality aims to: * Help to open/bind netlink sockets * Discover generic netlink protocols and multicast groups * Construct, encode and decode netlink messages Maybe the simplest usecase is to monitor events. Disk quota events:: from pyroute2 import DQuotSocket # DQuotSocket automatically performs discovery and binding, # since it has no other functionality beside of the monitoring with DQuotSocket() as ds: for message in ds.get(): print(message) Or IPRoute:: from pyroute2 import IPRoute with IPRoute() as ipr: # With IPRoute objects you have to call bind() manually ipr.bind() for message in ipr.get(): print(message) rtnetlink sample ---------------- More samples you can read in the project documentation. Low-level **IPRoute** utility --- Linux network configuration. The **IPRoute** class is a 1-to-1 RTNL mapping. There are no implicit interface lookups and so on. Some examples:: from socket import AF_INET from pyroute2 import IPRoute # get access to the netlink socket ip = IPRoute() # no monitoring here -- thus no bind() # print interfaces print(ip.get_links()) # create VETH pair and move v0p1 to netns 'test' ip.link_create(ifname='v0p0', peer='v0p1', kind='veth') idx = ip.link_lookup(ifname='v0p1')[0] ip.link('set', index=idx, net_ns_fd='test') # bring v0p0 up and add an address idx = ip.link_lookup(ifname='v0p0')[0] ip.link('set', index=idx, state='up') ip.addr('add', index=idx, address='10.0.0.1', broadcast='10.0.0.255', prefixlen=24) # create a route with metrics ip.route('add', dst='172.16.0.0/24', gateway='10.0.0.10', metrics={'mtu': 1400, 'hoplimit': 16}) # create MPLS lwtunnel # $ sudo modprobe mpls_iptunnel ip.route('add', dst='172.16.0.0/24', oif=idx, encap={'type': 'mpls', 'labels': '200/300'}) # create MPLS route: push label # $ sudo modprobe mpls_router # $ sudo sysctl net.mpls.platform_labels=1024 ip.route('add', family=AF_MPLS, oif=idx, dst=0x200, newdst=[0x200, 0x300]) # release Netlink socket ip.close() High-level transactional interface, **IPDB**, a network settings DB:: from pyroute2 import IPDB # # The `with` statement automatically calls `IPDB.release()` # in the case of an exception. with IPDB() as ip: # # Create bridge and add ports and addresses. # # Transaction will be started by `with` statement # and will be committed at the end of the block with ip.create(kind='bridge', ifname='rhev') as i: i.add_port('em1') i.add_port('em2') i.add_ip('10.0.0.2/24') # --> <-- Here the system state is as described in # the transaction, if no error occurs. If # there is an error, all the changes will be # rolled back. The IPDB arch allows to use it transparently with network namespaces:: from pyroute2 import IPDB from pyroute2 import NetNS # Create IPDB to work with the 'test' ip netns. # # Pls notice, that IPDB itself will work in the # main netns, only the netlink transport is # working in the namespace `test`. ip = IPDB(nl=NetNS('test')) # Wait until someone will set up ipaddr 127.0.0.1 # in the netns on the loopback device ip.interfaces.lo.wait_ip('127.0.0.1') # The IPDB object must be released before exit to # sync all the possible changes that are in progress. 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.7 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: License :: OSI Approved :: Apache Software License Classifier: Programming Language :: Python Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: System :: Networking Classifier: Topic :: System :: Systems Administration Classifier: Operating System :: POSIX :: Linux Classifier: Intended Audience :: Developers Classifier: Intended Audience :: System Administrators Classifier: Intended Audience :: Telecommunications Industry Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Development Status :: 4 - Beta pyroute2-0.4.21/tests/0000775000175000017500000000000013152333366014454 5ustar peetpeet00000000000000pyroute2-0.4.21/tests/unit/0000775000175000017500000000000013152333366015433 5ustar peetpeet00000000000000pyroute2-0.4.21/tests/unit/test_common.pyc0000644000175000017500000001305713127254575020511 0ustar peetpeet00000000000000ó Æ\Yc@sddlmZddlmZddlmZddlmZddlmZddlmZdefd„ƒYZd efd „ƒYZ d S( iÿÿÿÿ(tAddrPool(thexdump(thexload(tuuid32(tuifname(tdqn2intt TestAddrPoolcBsYeZd„Zd„Zd„Zd„Zd„Zd„Zd„Zd„Z d„Z RS( cCs\tddddƒ}xtdƒD]}|jƒq"Wy|jƒWntk rWnXdS(Ntminaddritmaxaddri(RtrangetalloctKeyError(tselftapti((s6/home/peet/Projects/pyroute2/tests/unit/test_common.pyttest_alloc_aligned s cCs\tddddƒ}xtdƒD]}|jƒq"Wy|jƒWntk rWnXdS(NRiRiü(RR R R (R R R((s6/home/peet/Projects/pyroute2/tests/unit/test_common.pyttest_alloc_odds cCsTtdddddtƒ}x2tdƒD]$}|jƒ|jƒks(t‚q(WdS(NRiRitreversei(RtTrueR R tAssertionError(R R R((s6/home/peet/Projects/pyroute2/tests/unit/test_common.pyt test_reverse!scCs2tddddƒ}|jƒ}|j|ƒdS(NRiRi(RR tfree(R R tf((s6/home/peet/Projects/pyroute2/tests/unit/test_common.pyt test_free's cCs>tddddƒ}y|jdƒWntk r9nXdS(NRiRii(RRR (R R ((s6/home/peet/Projects/pyroute2/tests/unit/test_common.pyttest_free_fail-s  cCsDtdddddtƒ}y|jdƒWntk r?nXdS(NRiRiRi(RRRR (R R ((s6/home/peet/Projects/pyroute2/tests/unit/test_common.pyttest_free_reverse_fail5s  c Cs£tƒ}|jƒ}|j|ƒ\}}}|j|dƒ\}}}||ks[t‚||dksqt‚|s}t‚| sŠt‚|jdksŸt‚dS(Ni(RR tlocateRt allocated( R R Rtbase1tbit1t is_allocated1tbase2tbit2t is_allocated2((s6/home/peet/Projects/pyroute2/tests/unit/test_common.pyt test_locate=s    cCs÷tƒ}|jƒ}|j|dƒ\}}}| s>t‚|jdksSt‚|j|ddƒ|j|dƒ\}}}|st‚|jdks€t‚|j|dƒ|j|dƒ\}}}| sÞt‚|jdksót‚dS(NiRi(RR RRRtsetaddrR(R R Rtbasetbitt is_allocated((s6/home/peet/Projects/pyroute2/tests/unit/test_common.pyttest_setaddr_allocatedJs     cCstƒ}|jƒ}|j|dƒ\}}}| s>t‚|jdksSt‚|j|ddƒ|j|dƒ\}}}| st‚|jdks¥t‚|j|dƒ|j|ƒ\}}}| sÚt‚|jdksït‚y|j|ƒWntk rnXdS(NiRi(RR RRRR#RR (R R RR$R%R&((s6/home/peet/Projects/pyroute2/tests/unit/test_common.pyttest_setaddr_freeZs"      ( t__name__t __module__RRRRRRR"R'R((((s6/home/peet/Projects/pyroute2/tests/unit/test_common.pyR s     t TestCommoncBs,eZd„Zd„Zd„Zd„ZRS(cCsÔd}t|ƒ}t|ddƒ}t|ƒdks<t‚t|ƒdksTt‚|d|dko‘|dko‘|dko‘dknsœt‚t|ƒ|ksŽt‚t|ƒ|d ksÐt‚dS( Nt abcdef5678tlengthiiiiiýÿÿÿt:(RtlenRR(R tbinarytdump1tdump2((s6/home/peet/Projects/pyroute2/tests/unit/test_common.pyt test_hexdumpqs #cCsˆtƒ}tƒ}tjdtƒ}t||ƒs9t‚t||ƒsNt‚||ks`t‚|dksrt‚|dks„t‚dS(NtlongI(Rt __builtins__tgettintt isinstanceR(R tuAtuBtprime((s6/home/peet/Projects/pyroute2/tests/unit/test_common.pyt test_uuid32€s  cCsLtdƒdkst‚tdƒdks0t‚tdƒdksHt‚dS(Ns 255.255.255.0is 255.240.0.0i s 255.0.0.0i(RR(R ((s6/home/peet/Projects/pyroute2/tests/unit/test_common.pyt test_dqn2int‹scCsTtƒ}tƒ}||ks$t‚t|ddƒt|ddƒksPt‚dS(Nii(RRR7(R tnAtnB((s6/home/peet/Projects/pyroute2/tests/unit/test_common.pyt test_uifnames  (R)R*R3R<R=R@(((s6/home/peet/Projects/pyroute2/tests/unit/test_common.pyR+os  N( tpyroute2.commonRRRRRRtobjectRR+(((s6/home/peet/Projects/pyroute2/tests/unit/test_common.pytsfpyroute2-0.4.21/tests/unit/test_common.py0000664000175000017500000000716413127143031020332 0ustar peetpeet00000000000000from pyroute2.common import AddrPool from pyroute2.common import hexdump from pyroute2.common import hexload from pyroute2.common import uuid32 from pyroute2.common import uifname from pyroute2.common import dqn2int class TestAddrPool(object): def test_alloc_aligned(self): ap = AddrPool(minaddr=1, maxaddr=1024) for i in range(1024): ap.alloc() try: ap.alloc() except KeyError: pass def test_alloc_odd(self): ap = AddrPool(minaddr=1, maxaddr=1020) for i in range(1020): ap.alloc() try: ap.alloc() except KeyError: pass def test_reverse(self): ap = AddrPool(minaddr=1, maxaddr=1024, reverse=True) for i in range(512): assert ap.alloc() > ap.alloc() def test_free(self): ap = AddrPool(minaddr=1, maxaddr=1024) f = ap.alloc() ap.free(f) def test_free_fail(self): ap = AddrPool(minaddr=1, maxaddr=1024) try: ap.free(0) except KeyError: pass def test_free_reverse_fail(self): ap = AddrPool(minaddr=1, maxaddr=1024, reverse=True) try: ap.free(0) except KeyError: pass def test_locate(self): ap = AddrPool() f = ap.alloc() base1, bit1, is_allocated1 = ap.locate(f) base2, bit2, is_allocated2 = ap.locate(f + 1) assert base1 == base2 assert bit2 == bit1 + 1 assert is_allocated1 assert not is_allocated2 assert ap.allocated == 1 def test_setaddr_allocated(self): ap = AddrPool() f = ap.alloc() base, bit, is_allocated = ap.locate(f + 1) assert not is_allocated assert ap.allocated == 1 ap.setaddr(f + 1, 'allocated') base, bit, is_allocated = ap.locate(f + 1) assert is_allocated assert ap.allocated == 2 ap.free(f + 1) base, bit, is_allocated = ap.locate(f + 1) assert not is_allocated assert ap.allocated == 1 def test_setaddr_free(self): ap = AddrPool() f = ap.alloc() base, bit, is_allocated = ap.locate(f + 1) assert not is_allocated assert ap.allocated == 1 ap.setaddr(f + 1, 'free') base, bit, is_allocated = ap.locate(f + 1) assert not is_allocated assert ap.allocated == 1 ap.setaddr(f, 'free') base, bit, is_allocated = ap.locate(f) assert not is_allocated assert ap.allocated == 0 try: ap.free(f) except KeyError: pass class TestCommon(object): def test_hexdump(self): binary = b'abcdef5678' dump1 = hexdump(binary) dump2 = hexdump(binary, length=6) assert len(dump1) == 29 assert len(dump2) == 17 assert dump1[2] == \ dump1[-3] == \ dump2[2] == \ dump2[-3] == ':' assert hexload(dump1) == binary assert hexload(dump2) == binary[:6] def test_uuid32(self): uA = uuid32() uB = uuid32() prime = __builtins__.get('long', int) assert isinstance(uA, prime) assert isinstance(uB, prime) assert uA != uB assert uA < 0x100000000 assert uB < 0x100000000 def test_dqn2int(self): assert dqn2int('255.255.255.0') == 24 assert dqn2int('255.240.0.0') == 12 assert dqn2int('255.0.0.0') == 8 def test_uifname(self): nA = uifname() nB = uifname() assert nA != nB assert int(nA[2:], 16) != int(nB[2:], 16) pyroute2-0.4.21/tests/unit/__pycache__/0000775000175000017500000000000013152333366017643 5ustar peetpeet00000000000000pyroute2-0.4.21/tests/unit/__pycache__/test_common.cpython-35.pyc0000644000175000017500000001167513127255711024626 0ustar peetpeet00000000000000 Æ\Ytã@sddlmZddlmZddlmZddlmZddlmZddlmZGdd„deƒZGd d „d eƒZ d S) é)ÚAddrPool)Úhexdump)Úhexload)Úuuid32)Úuifname)Údqn2intc@s|eZdZdd„Zdd„Zdd„Zdd„Zd d „Zd d „Zd d„Z dd„Z dd„Z dS)Ú TestAddrPoolc Cs]tddddƒ}xtdƒD]}|jƒq"Wy|jƒWntk rXYnXdS)NÚminaddréÚmaxaddri)rÚrangeÚallocÚKeyError)ÚselfÚapÚi©rú6/home/peet/Projects/pyroute2/tests/unit/test_common.pyÚtest_alloc_aligned s zTestAddrPool.test_alloc_alignedc Cs]tddddƒ}xtdƒD]}|jƒq"Wy|jƒWntk rXYnXdS)Nr r r iü)rr r r)rrrrrrÚtest_alloc_odds zTestAddrPool.test_alloc_oddcCsTtddddddƒ}x2tdƒD]$}|jƒ|jƒks(t‚q(WdS)Nr r r iÚreverseTi)rr r ÚAssertionError)rrrrrrÚ test_reverse!szTestAddrPool.test_reversecCs2tddddƒ}|jƒ}|j|ƒdS)Nr r r i)rr Úfree)rrÚfrrrÚ test_free's zTestAddrPool.test_freec Cs?tddddƒ}y|jdƒWntk r:YnXdS)Nr r r ir)rrr)rrrrrÚtest_free_fail-s  zTestAddrPool.test_free_failc CsEtddddddƒ}y|jdƒWntk r@YnXdS)Nr r r irTr)rrr)rrrrrÚtest_free_reverse_fail5s  z#TestAddrPool.test_free_reverse_failc Cs£tƒ}|jƒ}|j|ƒ\}}}|j|dƒ\}}}||ks[t‚||dksqt‚|s}t‚| sŠt‚|jdksŸt‚dS)Nr )rr ÚlocaterÚ allocated) rrrZbase1Zbit1Z is_allocated1Zbase2Zbit2Z is_allocated2rrrÚ test_locate=s    zTestAddrPool.test_locatecCs÷tƒ}|jƒ}|j|dƒ\}}}| s>t‚|jdksSt‚|j|ddƒ|j|dƒ\}}}|st‚|jdks€t‚|j|dƒ|j|dƒ\}}}| sÞt‚|jdksót‚dS)Nr ré)rr rrrÚsetaddrr)rrrÚbaseÚbitÚ is_allocatedrrrÚtest_setaddr_allocatedJs     z#TestAddrPool.test_setaddr_allocatedc Cstƒ}|jƒ}|j|dƒ\}}}| s>t‚|jdksSt‚|j|ddƒ|j|dƒ\}}}| st‚|jdks¥t‚|j|dƒ|j|ƒ\}}}| sÚt‚|jdksït‚y|j|ƒWntk rYnXdS)Nr rr)rr rrrr"rr)rrrr#r$r%rrrÚtest_setaddr_freeZs"      zTestAddrPool.test_setaddr_freeN) Ú__name__Ú __module__Ú __qualname__rrrrrrr r&r'rrrrr s      rc@s@eZdZdd„Zdd„Zdd„Zdd„Zd S) Ú TestCommoncCsÚd}t|ƒ}t|ddƒ}t|ƒdks<t‚t|ƒdksTt‚|d|d ko‘|dko‘|d ko‘dknsœt‚t|ƒ|ksŽt‚t|ƒ|dd…ksÖt‚dS) Ns abcdef5678Úlengthééér!éú:éýÿÿÿr2)rÚlenrr)rÚbinaryZdump1Zdump2rrrÚ test_hexdumpqs #zTestCommon.test_hexdumpcCsˆtƒ}tƒ}tjdtƒ}t||ƒs9t‚t||ƒsNt‚||ks`t‚|dksrt‚|dks„t‚dS)NÚlongl)rÚ __builtins__ÚgetÚintÚ isinstancer)rZuAZuBZprimerrrÚ test_uuid32€s  zTestCommon.test_uuid32cCsLtdƒdkst‚tdƒdks0t‚tdƒdksHt‚dS)Nz 255.255.255.0éz 255.240.0.0é z 255.0.0.0é)rr)rrrrÚ test_dqn2int‹szTestCommon.test_dqn2intcCs`tƒ}tƒ}||ks$t‚t|dd…dƒt|dd…dƒks\t‚dS)Nr!é)rrr9)rZnAZnBrrrÚ test_uifnames  zTestCommon.test_uifnameN)r(r)r*r5r;r?rArrrrr+os   r+N) Zpyroute2.commonrrrrrrÚobjectrr+rrrrÚsfpyroute2-0.4.21/CHANGELOG.md0000664000175000017500000003731413152331177015131 0ustar peetpeet00000000000000changelog ========= * 0.4.21 * ipdb: #416 -- workaround for the vrf_table issue * 0.4.20 * ipdb: #392 -- fix MPLS route key reference * ipdb: #408 -- fix IPv6 routes in tables >= 256 * ipdb: use tos as a route key * 0.4.19 * ipdb: global methods `review()`, `drop()`, `dump()`, `load()` * ipdb: support VLAN protocol (802.1q, 802.1ad) * ipdb: initial support for IPv6 rules * ipdb: #388 -- normalize IPv6 addresses * ipdb: #391 -- support both IPv4 and IPv6 default routes * ipdb: #394 -- correctly work with route priorities * netns: non-destructive `netns.create()` * 0.4.18 * ipdb: #379 [critical] -- routes in global commits * ipdb: #380 -- global commit with disabled plugins * ipdb: #381 -- exceptions fixed * ipdb: #382 -- manage dependent routes during interface commits * ipdb: #384 -- global `review()` * ipdb: #385 -- global `drop()` * netns: #383 -- support ppc64 * general: public API refactored (same signatures; to be documented) * 0.4.17 * req: #374 [critical] -- mode nla init * iproute: #378 [critical] -- fix `flush_routes()` to respect filters * ifinfmsg: #376 -- fix data plugins API to support pyinstaller * 0.4.16 * ipdb: race fixed: remove port/bridge * ipdb: #280 -- race fixed: port/bridge * ipdb: #302 -- ipaddr views: [ifname].ipaddr.ipv4, [ifname]ipaddr.ipv6 * ipdb: #357 -- allow bridge timings to have some delta * ipdb: #338 -- allow to fix interface objects from failed `create()` * rtnl: #336 -- fix vlan flags * iproute: #342 -- the match method takes any callable * nlsocket: #367 -- increase default SO_SNDBUF * ifinfmsg: support tuntap on armv6l, armv7l platforms * 0.4.15 * req: #365 -- full and short nla notation fixed, critical * iproute: #364 -- new method, `brport()` * ipdb: -- support bridge port options * 0.4.14 * event: new genl protocols set: VFS_DQUOT, acpi_event, thermal_event * ipdb: #310 -- fixed priority change on routes * ipdb: #349 -- fix setting ifalias on interfaces * ipdb: #353 -- mitigate kernel oops during bridge creation * ipdb: #354 -- allow to explicitly choose plugins to load * ipdb: #359 -- provide read-only context managers * rtnl: #336 -- vlan flags support * rtnl: #352 -- support interface type plugins * tc: #344 -- mirred action * tc: #346 -- connmark action * netlink: #358 -- memory optimization * config: #360 -- generic asyncio config * iproute: #362 -- allow to change or replace a qdisc * 0.4.13 * ipset: full rework of the IPSET_ATTR_DATA and IPSET_ATTR_ADT ACHTUNG: this commit may break API compatibility * ipset: hash:mac support * ipset: list:set support * ipdb: throw EEXIST when creates VLAN/VXLAN devs with same ID, but under different names * tests: #329 -- include unit tests into the bundle * legal: E/// logo removed * 0.4.12 * ipdb: #314 -- let users choose RTNL groups IPDB listens to * ipdb: #321 -- isolate `net_ns_.*` setup in a separate code block * ipdb: #322 -- IPv6 updates on interfaces in DOWN state * ifinfmsg: allow absolute/relative paths in the net_ns_fd NLA * ipset: #323 -- support setting counters on ipset add * ipset: `headers()` command * ipset: revisions * ipset: #326 -- mark types * 0.4.11 * rtnl: #284 -- support vlan_flags * ipdb: #288 -- do not inore link-local addresses * ipdb: #300 -- sort ip addresses * ipdb: #306 -- support net_ns_pid * ipdb: #307 -- fix IPv6 routes management * ipdb: #311 -- vlan interfaces address loading * iprsocket: #305 -- support NETLINK_LISTEN_ALL_NSID * 0.4.10 * devlink: fix fd leak on broken init * 0.4.9 * sock_diag: initial NETLINK_SOCK_DIAG support * rtnl: fix critical fd leak in the compat code * 0.4.8 * rtnl: compat proxying fix * 0.4.7 * rtnl: compat code is back * netns: custom netns path support * ipset: multiple improvements * 0.4.6 * ipdb: #278 -- fix initial ports mapping * ipset: #277 -- fix ADT attributes parsing * nl80211: #274, #275, #276 -- BSS-related fixes * 0.4.5 * ifinfmsg: GTP interfaces support * generic: devlink protocol support * generic: code cleanup * 0.4.4 * iproute: #262 -- `get_vlans()` fix * iproute: default mask 32 for IPv4 in `addr()` * rtmsg: #260 -- RTA_FLOW support * 0.4.3 * ipdb: #259 -- critical `Interface` class fix * benchmark: initial release * 0.4.2 * ipdb: event modules * ipdb: on-demand views * ipdb: rules management * ipdb: bridge controls * ipdb: #258 -- important Python compatibility fixes * netns: #257 -- pipe leak fix * netlink: support pickling for nlmsg * 0.4.1 * netlink: no buffer copying in the parser * netlink: parse NLA on demand * ipdb: #244 -- lwtunnel multipath fixes * iproute: #235 -- route types * docs updated * 0.4.0 * ACHTUNG: old kernels compatibility code is dropped * ACHTUNG: IPDB uses two separate sockets for monitoring and commands * ipdb: #244 -- multipath lwtunnel * ipdb: #242 -- AF_MPLS routes * ipdb: #241, #234 -- fix create(..., reuse=True) * ipdb: #239 -- route encap and metrics fixed * ipdb: #238 -- generic port management * ipdb: #235 -- support route scope and type * ipdb: #230, #232 -- routes GC (work in progress) * rtnl: #245 -- do not fail if `/proc/net/psched` doesn't exist * rtnl: #233 -- support VRF interfaces (requires net-next) * 0.3.21 * ipdb: #231 -- return `ipdb.common` as deprecated * 0.3.20 * iproute: `vlan_filter()` * iproute: #229 -- FDB management * general: exceptions re-exported via the root module * 0.3.19 * rtmsg: #227 -- MPLS lwtunnel basic support * iproute: `route()` docs updated * general: #228 -- exceptions layout changed * package-rh: rpm subpackages * 0.3.18 * version bump -- include docs in the release tarball * 0.3.17 * tcmsg: qdiscs and filters as plugins * tcmsg: #223 -- tc clsact and bpf direct-action * tcmsg: plug, codel, choke, drr qdiscs * tests: CI in VMs (see civm project) * tests: xunit output * ifinfmsg: tuntap support in i386, i686 * ifinfmsg: #207 -- support vlan filters * examples: #226 -- included in the release tarball * ipdb: partial commits, initial support * 0.3.16 * ipdb: fix the multiple IPs in one commit case * rtnl: support veth peer attributes * netns: support 32bit i686 * netns: fix MIPS support * netns: fix tun/tap creation * netns: fix interface move between namespaces * tcmsg: support hfsc, fq_codel, codel qdiscs * nftables: initial support * netlink: dump/load messages to/from simple types * 0.3.15 * netns: #194 -- fix fd leak * iproute: #184 -- fix routes dump * rtnl: TCA_ACT_BPF support * rtnl: ipvlan support * rtnl: OVS support removed * iproute: rule() improved to support all NLAs * project supported by Ericsson * 0.3.14 * package-rh: spec fixed * package-rh: both licenses added * remote: fixed the setup.py record * 0.3.13 * package-rh: new rpm for Fedora and CentOS * remote: new draft of the remote protocol * netns: refactored using the new remote protocol * ipdb: gretap support * 0.3.12 * ipdb: new `Interface.wait_ip()` routine * ipdb: #175 -- fix `master` attribute cleanup * ipdb: #171 -- support multipath routes * ipdb: memory consumption improvements * rtmsg: MPLS support * rtmsg: RTA_VIA support * iwutil: #174 -- fix FREQ_FIXED flag * 0.3.11 * ipdb: #161 -- fix memory allocations * nlsocket: #161 -- remove monitor mode * 0.3.10 * rtnl: added BPF filters * rtnl: LWtunnel support in ifinfmsg * ipdb: support address attributes * ipdb: global transactions, initial version * ipdb: routes refactored to use key index (speed up) * config: eventlet support embedded (thanks to Angus Lees) * iproute: replace tc classes * iproute: flush_addr(), flush_rules() * iproute: rule() refactored * netns: proxy file objects (stdin, stdout, stderr) * 0.3.9 * root imports: #109, #135 -- `issubclass`, `isinstance` * iwutil: multiple improvements * iwutil: initial tests * proxy: correctly forward NetlinkError * iproute: neighbour tables support * iproute: #147, filters on dump calls * config: initial usage of `capabilities` * 0.3.8 * docs: inheritance diagrams * nlsocket: #126, #132 -- resource deallocation * arch: #128, #131 -- MIPS support * setup.py: #133 -- syntax error during install on Python2 * 0.3.7 * ipdb: new routing syntax * ipdb: sync interface movement between namespaces * ipdb: #125 -- fix route metrics * netns: new class NSPopen * netns: #119 -- i386 syscall * netns: #122 -- return correct errno * netlink: #126 -- fix socket reuse * 0.3.6 * dhcp: initial release DHCPv4 * license: dual GPLv2+ and Apache v2.0 * ovs: port add/delete * macvlan, macvtap: basic support * vxlan: basic support * ipset: basic support * 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.4.21/README.packages.md0000664000175000017500000000070413123443516016344 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`. NOTE: for python3 you should have python3-pkgversion-macros installed, available both on Fedora and EPEL. pyroute2-0.4.21/setup.py0000664000175000017500000000541413130003470015012 0ustar peetpeet00000000000000#!/usr/bin/env python import os try: import configparser except ImportError: import ConfigParser as configparser # When one runs pip install from the git repo, the setup.ini # doesn't exist. But we still have here a full git repo with # all the git log and with the Makefile. # # So just try to use it. try: os.stat('setup.ini') except: os.system('make force-version') config = configparser.ConfigParser() config.read('setup.ini') module = __import__(config.get('setup', 'setuplib'), globals(), locals(), ['setup'], 0) setup = getattr(module, 'setup') readme = open("README.md", "r") setup(name='pyroute2', version=config.get('setup', 'release'), description='Python Netlink library', author='Peter V. Saveliev', author_email='peter@svinota.eu', url='https://github.com/svinota/pyroute2', license='dual license GPLv2+ and Apache v2', packages=['pyroute2', 'pyroute2.cli', 'pyroute2.config', 'pyroute2.dhcp', 'pyroute2.ipdb', 'pyroute2.netns', 'pyroute2.netns.process', 'pyroute2.netlink', 'pyroute2.netlink.generic', 'pyroute2.netlink.ipq', 'pyroute2.netlink.nfnetlink', 'pyroute2.netlink.rtnl', 'pyroute2.netlink.rtnl.ifinfmsg', 'pyroute2.netlink.rtnl.ifinfmsg.plugins', 'pyroute2.netlink.rtnl.tcmsg', 'pyroute2.netlink.taskstats', 'pyroute2.netlink.nl80211', 'pyroute2.netlink.devlink', 'pyroute2.netlink.diag', 'pyroute2.netlink.event', 'pyroute2.protocols', 'pyroute2.remote'], classifiers=['License :: OSI Approved :: GNU General Public ' + 'License v2 or later (GPLv2+)', 'License :: OSI Approved :: Apache Software License', 'Programming Language :: Python', 'Topic :: Software Development :: Libraries :: ' + 'Python Modules', 'Topic :: System :: Networking', 'Topic :: System :: Systems Administration', 'Operating System :: POSIX :: Linux', 'Intended Audience :: Developers', 'Intended Audience :: System Administrators', 'Intended Audience :: Telecommunications Industry', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Development Status :: 4 - Beta'], long_description=readme.read()) pyroute2-0.4.21/README.report.md0000664000175000017500000000132413123443516016100 0ustar peetpeet00000000000000report a bug ============ In the case you have issues, please report them to the project bug tracker: https://github.com/svinota/pyroute2/issues It is important to provide all the required information with your report: * Linux kernel version * Python version * Specific environment, if used -- gevent, eventlet etc. Sometimes it is needed to measure specific system parameters. There is a code to do that, e.g.:: $ sudo make test-platform Please keep in mind, that this command will try to create and delete different interface types. It is possible also to run the test in your code:: from pprint import pprint from pyroute2.config.test_platform import TestCapsRtnl pprint(TestCapsRtnl().collect()) pyroute2-0.4.21/README.md0000664000175000017500000001412513152331050014560 0ustar peetpeet00000000000000pyroute2 ======== Pyroute2 is a pure Python **netlink** library. It requires only Python stdlib, no 3rd party libraries. The library was started as an RTNL protocol implementation, so the name is **pyroute2**, but now it supports many netlink protocols. Some supported netlink families and protocols: * **rtnl**, network settings --- addresses, routes, traffic controls * **nfnetlink** --- netfilter API: **ipset**, **nftables**, ... * **ipq** --- simplest userspace packet filtering, iptables QUEUE target * **devlink** --- manage and monitor devlink-enabled hardware * **generic** --- generic netlink families * **nl80211** --- wireless functions API (basic support) * **taskstats** --- extended process statistics * **acpi_events** --- ACPI events monitoring * **thermal_events** --- thermal events monitoring * **VFS_DQUOT** --- disk quota events monitoring the simplest usecase -------------------- The socket objects, provided by the library, are actual socket objects with a little bit extended API. The additional functionality aims to: * Help to open/bind netlink sockets * Discover generic netlink protocols and multicast groups * Construct, encode and decode netlink messages Maybe the simplest usecase is to monitor events. Disk quota events:: from pyroute2 import DQuotSocket # DQuotSocket automatically performs discovery and binding, # since it has no other functionality beside of the monitoring with DQuotSocket() as ds: for message in ds.get(): print(message) Or IPRoute:: from pyroute2 import IPRoute with IPRoute() as ipr: # With IPRoute objects you have to call bind() manually ipr.bind() for message in ipr.get(): print(message) rtnetlink sample ---------------- More samples you can read in the project documentation. Low-level **IPRoute** utility --- Linux network configuration. The **IPRoute** class is a 1-to-1 RTNL mapping. There are no implicit interface lookups and so on. Some examples:: from socket import AF_INET from pyroute2 import IPRoute # get access to the netlink socket ip = IPRoute() # no monitoring here -- thus no bind() # print interfaces print(ip.get_links()) # create VETH pair and move v0p1 to netns 'test' ip.link_create(ifname='v0p0', peer='v0p1', kind='veth') idx = ip.link_lookup(ifname='v0p1')[0] ip.link('set', index=idx, net_ns_fd='test') # bring v0p0 up and add an address idx = ip.link_lookup(ifname='v0p0')[0] ip.link('set', index=idx, state='up') ip.addr('add', index=idx, address='10.0.0.1', broadcast='10.0.0.255', prefixlen=24) # create a route with metrics ip.route('add', dst='172.16.0.0/24', gateway='10.0.0.10', metrics={'mtu': 1400, 'hoplimit': 16}) # create MPLS lwtunnel # $ sudo modprobe mpls_iptunnel ip.route('add', dst='172.16.0.0/24', oif=idx, encap={'type': 'mpls', 'labels': '200/300'}) # create MPLS route: push label # $ sudo modprobe mpls_router # $ sudo sysctl net.mpls.platform_labels=1024 ip.route('add', family=AF_MPLS, oif=idx, dst=0x200, newdst=[0x200, 0x300]) # release Netlink socket ip.close() High-level transactional interface, **IPDB**, a network settings DB:: from pyroute2 import IPDB # # The `with` statement automatically calls `IPDB.release()` # in the case of an exception. with IPDB() as ip: # # Create bridge and add ports and addresses. # # Transaction will be started by `with` statement # and will be committed at the end of the block with ip.create(kind='bridge', ifname='rhev') as i: i.add_port('em1') i.add_port('em2') i.add_ip('10.0.0.2/24') # --> <-- Here the system state is as described in # the transaction, if no error occurs. If # there is an error, all the changes will be # rolled back. The IPDB arch allows to use it transparently with network namespaces:: from pyroute2 import IPDB from pyroute2 import NetNS # Create IPDB to work with the 'test' ip netns. # # Pls notice, that IPDB itself will work in the # main netns, only the netlink transport is # working in the namespace `test`. ip = IPDB(nl=NetNS('test')) # Wait until someone will set up ipaddr 127.0.0.1 # in the netns on the loopback device ip.interfaces.lo.wait_ip('127.0.0.1') # The IPDB object must be released before exit to # sync all the possible changes that are in progress. 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.7 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.4.21/examples/0000775000175000017500000000000013152333366015130 5ustar peetpeet00000000000000pyroute2-0.4.21/examples/ipdb_chain.py0000664000175000017500000000141413123443516017557 0ustar peetpeet00000000000000# An example how to use command chaining in IPDB from pyroute2 import IPDB from pyroute2.common import uifname # unique names -- for the testing bo0 = uifname() p0 = uifname() p1 = uifname() with IPDB() as ip: # create bonding ip.create(ifname=bo0, kind='bond', bond_mode=2).commit() # create slave ports ip.create(ifname=p0, kind='dummy').commit() ip.create(ifname=p1, kind='dummy').commit() # set up bonding ip.interfaces[bo0].add_port(ip.interfaces[p0]).\ add_port(ip.interfaces[p1]).\ add_ip('172.16.0.1/24').\ add_ip('172.16.0.2/24').\ option('mtu', 1400).\ up().\ commit() for i in (p0, p1, bo0): try: ip.interfaces[i].remove().commit() except: pass pyroute2-0.4.21/examples/nl80211_interface_type.py0000664000175000017500000000072013123443516021564 0ustar peetpeet00000000000000import errno from pyroute2 import IW from pyroute2 import IPRoute from pyroute2.netlink.exceptions import NetlinkError # interface name to check ifname = 'lo' ip = IPRoute() iw = IW() index = ip.link_lookup(ifname=ifname)[0] try: iw.get_interface_by_ifindex(index) print("wireless interface") except NetlinkError as e: if e.code == errno.ENODEV: # 19 'No such device' print("not a wireless interface") finally: iw.close() ip.close() pyroute2-0.4.21/examples/ipq.py0000664000175000017500000000060713123443516016273 0ustar peetpeet00000000000000from pyroute2.common import hexdump from pyroute2 import IPQSocket from pyroute2.netlink.ipq import NF_ACCEPT from dpkt.ip import IP ip = IPQSocket() ip.bind() try: while True: msg = ip.get()[0] print("\n") print(hexdump(msg.raw)) print(repr(IP(msg['payload']))) ip.verdict(msg['packet_id'], NF_ACCEPT) except: pass finally: ip.release() pyroute2-0.4.21/examples/pmonitor.py0000664000175000017500000000063613123443516017353 0ustar peetpeet00000000000000''' Monitor process exit ''' from pyroute2 import TaskStats from pyroute2.common import hexdump pmask = '' with open('/proc/cpuinfo', 'r') as f: for line in f.readlines(): if line.startswith('processor'): pmask += ',' + line.split()[2] pmask = pmask[1:] ts = TaskStats() ts.register_mask(pmask) msg = ts.get()[0] print(hexdump(msg.raw)) print(msg) ts.deregister_mask(pmask) ts.release() pyroute2-0.4.21/examples/generic/0000775000175000017500000000000013152333366016544 5ustar peetpeet00000000000000pyroute2-0.4.21/examples/generic/netl.c0000664000175000017500000000675113127143031017651 0ustar peetpeet00000000000000/* * Generic netlink sample -- kernel module * Use `make` to compile and `insmod` to load the module * * Sergiy Lozovsky * Peter V. Saveliev * * Requires kernel 4.10+ */ #include /* Needed by all modules */ #include /* Needed for KERN_INFO */ #include /* Needed for the macros */ #include /* attributes (variables): the index in this enum is used as a reference for the type, * userspace application has to indicate the corresponding type * the policy is used for security considerations */ enum { EXMPL_NLA_UNSPEC, EXMPL_NLA_DATA, EXMPL_NLA_LEN, __EXMPL_NLA_MAX, }; /* ... and the same for commands */ enum { EXMPL_CMD_UNSPEC, EXMPL_CMD_MSG, }; /* attribute policy: defines which attribute has which type (e.g int, char * etc) * possible values defined in net/netlink.h */ static struct nla_policy exmpl_genl_policy[__EXMPL_NLA_MAX] = { [EXMPL_NLA_DATA] = { .type = NLA_NUL_STRING }, [EXMPL_NLA_LEN] = { .type = NLA_U32 }, }; #define VERSION_NR 1 static struct genl_family exmpl_gnl_family; static int get_length(struct sk_buff *request, struct genl_info *info) { struct sk_buff *reply; char *buffer; void *msg_head; if (info == NULL) return -EINVAL; if (!info->attrs[EXMPL_NLA_DATA]) return -EINVAL; /* get the data */ buffer = nla_data(info->attrs[EXMPL_NLA_DATA]); /* send a message back*/ /* allocate some memory, since the size is not yet known use NLMSG_GOODSIZE*/ reply = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); if (reply == NULL) return -ENOMEM; /* start the message */ msg_head = genlmsg_put_reply(reply, info, &exmpl_gnl_family, 0, info->genlhdr->cmd); if (msg_head == NULL) { return -ENOMEM; } /* add a EXMPL_LEN attribute -- report the data length */ if (0 != nla_put_u32(reply, EXMPL_NLA_LEN, strlen(buffer))) return -EINVAL; /* finalize the message */ genlmsg_end(reply, msg_head); /* send the message back */ if (0 != genlmsg_reply(reply, info)) return -EINVAL; return 0; } /* commands: mapping between commands and actual functions*/ static const struct genl_ops exmpl_gnl_ops_echo[] = { { .cmd = EXMPL_CMD_MSG, .policy = exmpl_genl_policy, .doit = get_length, }, }; /* family definition */ static struct genl_family exmpl_gnl_family __ro_after_init = { .name = "EXMPL_GENL", //the name of this family, used by userspace application .version = VERSION_NR, //version number .maxattr = __EXMPL_NLA_MAX - 1, .module = THIS_MODULE, .ops = exmpl_gnl_ops_echo, .n_ops = ARRAY_SIZE(exmpl_gnl_ops_echo), }; static int __init exmpl_gnl_init(void) { int rc; rc = genl_register_family(&exmpl_gnl_family); if (rc != 0) { printk(KERN_INFO "rkmod: genl_register_family failed %d\n", rc); return 1; } printk(KERN_INFO "Generic netlink example loaded, protocol version %d\n", VERSION_NR); return 0; } static void __exit exmpl_gnl_exit(void) { int ret; /*unregister the family*/ ret = genl_unregister_family(&exmpl_gnl_family); if(ret !=0){ printk("unregister family %i\n",ret); } } module_init(exmpl_gnl_init); module_exit(exmpl_gnl_exit); MODULE_LICENSE("GPL"); pyroute2-0.4.21/examples/generic/netl.py0000775000175000017500000000233013127143031020047 0ustar peetpeet00000000000000#!/usr/bin/env python import traceback from pyroute2.netlink import NLM_F_REQUEST from pyroute2.netlink import genlmsg from pyroute2.netlink.generic import GenericNetlinkSocket RLINK_CMD_UNSPEC = 0 RLINK_CMD_REQ = 1 class rcmd(genlmsg): ''' Message class that will be used to communicate with the kernel module ''' nla_map = (('RLINK_ATTR_UNSPEC', 'none'), ('RLINK_ATTR_DATA', 'asciiz'), ('RLINK_ATTR_LEN', 'uint32')) class Rlink(GenericNetlinkSocket): def send_data(self, data): msg = rcmd() msg['cmd'] = RLINK_CMD_REQ msg['version'] = 1 msg['attrs'] = [('RLINK_ATTR_DATA', data)] ret = self.nlm_request(msg, self.prid, msg_flags=NLM_F_REQUEST)[0] return ret.get_attr('RLINK_ATTR_LEN') if __name__ == '__main__': try: # create protocol instance rlink = Rlink() rlink.bind('EXMPL_GENL', rcmd) # request a method print(rlink.send_data('x' * 65000)) except: # if there was an error, log it to the console traceback.print_exc() finally: # finally -- release the instance rlink.close() pyroute2-0.4.21/examples/generic/Makefile0000664000175000017500000000023313123443516020177 0ustar peetpeet00000000000000obj-m += netl.o all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean pyroute2-0.4.21/examples/generic/netl.pyc0000664000175000017500000000273513116636557020242 0ustar peetpeet00000000000000ó .=;Yc@sÆddlZddlmZddlmZddlmZdZdZdefd„ƒYZd efd „ƒYZ z@y(e ƒZ e j d eƒe j ƒGHWnej ƒnXWde jƒXdS( iÿÿÿÿN(t NLM_F_REQUEST(tgenlmsg(tGenericNetlinkSocketiitrcmdcBseZdZddfZRS(sS Message class that will be used to communicate with the kernel module tRLINK_ATTR_UNSPECtnonetRLINK_ATTR_DATAtasciiz(RR(RR(t__name__t __module__t__doc__tnla_map(((snetl.pyR stRlinkcBseZdZd„ZRS(s Custom generic netlink protocol. Has one method, `hello_world()`, the only netlink call of the kernel module. cCsItƒ}t|ds   pyroute2-0.4.21/examples/cli/0000775000175000017500000000000013152333366015677 5ustar peetpeet00000000000000pyroute2-0.4.21/examples/cli/test_ensure0000664000175000017500000000102713130003470020144 0ustar peetpeet00000000000000! ! Create a dummy interface with an address on it. ! Notice that the interface doesn't appear on the ! system before the commit call. ! create ifname=test01 kind=dummy interfaces test01 add_ip 172.16.189.5/24 up commit ! Rollback any transaction that makes the address ! unavailable: ! ensure reachable=172.16.189.5 ! Try to remove the interface, the transaction ! must fail: ! interfaces test01 remove commit ! Here we check with external tools that the ! interface still exists. ! pyroute2-0.4.21/examples/cli/test_comments_hash0000664000175000017500000000036513130003470021477 0ustar peetpeet00000000000000# # Test comments start with # # create ifname=test01 kind=dummy address=00:11:22:33:44:55 commit # interfaces # ... tail comments # # ... indented comments # test01 # dump # remove commit pyroute2-0.4.21/examples/cli/test_comments_bang0000664000175000017500000000036513130003470021463 0ustar peetpeet00000000000000! ! Test comments start with ! ! create ifname=test01 kind=dummy address=00:11:22:33:44:55 commit ! interfaces ! ... tail comments ! ! ... indented comments ! test01 ! dump ! remove commit pyroute2-0.4.21/examples/cli/test_comments_mixed0000664000175000017500000000037413130003470021662 0ustar peetpeet00000000000000# ! Test mixed comments, both ! and # # create ifname=test01 kind=dummy address=00:11:22:33:44:55 commit ! interfaces # ... tail comments ! # ... indented comments ! test01 # dump ! remove commit pyroute2-0.4.21/examples/cli/test_dump_lo0000664000175000017500000000011013130003470020272 0ustar peetpeet00000000000000! ! Just dump the loopback interface. ! interfaces lo dump pyroute2-0.4.21/examples/create_vlan.py0000664000175000017500000000202113123443516017755 0ustar peetpeet00000000000000''' Example: python ./examples/create_vlan.py [master] Master is an interface to add VLAN to, e.g. eth0 or tap0 or whatever else. Without parameters use tap0 as the default. ''' from pyroute2 import IPDB from pyroute2.common import uifname ip = IPDB() try: # unique interface names ms = uifname() vn = uifname() master = ip.create(ifname=ms, kind='dummy').commit() with ip.create(kind='vlan', ifname=vn, link=master, vlan_id=101) as i: # Arguments for ip.create() are executed before the transaction, # in the IPRoute.link('add', ...) call. If create() fails, the # interface became invalid and is not operable, you can safely # drop it. # # Here goes the rest of transaction. If it fails, the interface # continues to work, only failed transaction gets dropped. i.add_ip('10.251.0.1', 24) i.add_ip('10.251.0.2', 24) i.mtu = 1400 finally: try: ip.interfaces[ms].remove().commit() except: pass ip.release() pyroute2-0.4.21/examples/taskstats.py0000664000175000017500000000036613123443516017525 0ustar peetpeet00000000000000''' Simple taskstats sample. ''' import os from pyroute2 import TaskStats pid = os.getpid() ts = TaskStats() # bind is required in the case of generic netlink ts.bind() ret = ts.get_pid_stat(int(pid))[0] # parsed structure print(ret) ts.close() pyroute2-0.4.21/examples/custom_socket_base.py0000664000175000017500000000402613127143031021346 0ustar peetpeet00000000000000### # # The `socket.socket` class is not sublcass-friendly, and sometimes it is # better to use a custom wrapper providing socket API, than the original # socket class. # # But some projects, that use pyroute2, already have their own solutions, # and providing the library-wide wrapper breaks the behaviour of these # projects. # # So we provide a way to define a custom `SocketBase` class, that will be # used as base for the `NetlinkSocket` # import types from socket import socket from functools import partial from pyroute2 import config from pyroute2 import netns from pyroute2 import NetNS ### # # socket.socket isn't very subclass-friendly, so wrap it instead. # class SocketWrapper(object): def __init__(self, *args, **kwargs): _socketmethods = ( 'bind', 'close', 'connect', 'connect_ex', 'listen', 'getpeername', 'getsockname', 'getsockopt', 'makefile', 'recv', 'recvfrom', 'recv_into', 'recvfrom_into', 'send', 'sendto', 'sendall', 'setsockopt', 'setblocking', 'settimeout', 'gettimeout', 'shutdown') _sock = kwargs.get('_sock', None) or socket(*args, **kwargs) self._sock = _sock print("Custom socket wrapper init done") def _forward(name, self, *args, **kwargs): print("Forward <%s> method" % name) return getattr(self._sock, name)(*args, **kwargs) for name in _socketmethods: f = partial(_forward, name) f.__name__ = name setattr(SocketWrapper, name, types.MethodType(f, self)) def fileno(self): # for some obscure reason, we can not implement `fileno()` # proxying as above, so just make a hardcore version return self._sock.fileno() def dup(self): return self.__class__(_sock=self._sock.dup()) config.SocketBase = SocketWrapper print(netns.listnetns()) ### # # Being run via the root module, real IPRoute import is postponed, # to inspect the code, refer to `pyroute2/__init__.py` # ns = NetNS('test') print(ns.get_addr()) ns.close() pyroute2-0.4.21/examples/devlink_port_list.py0000664000175000017500000000041313123443516021230 0ustar peetpeet00000000000000from pyroute2 import DL dl = DL() for q in dl.get_port_dump(): print('%s\t%s\t%u' % (q.get_attr('DEVLINK_ATTR_BUS_NAME'), q.get_attr('DEVLINK_ATTR_DEV_NAME'), q.get_attr('DEVLINK_ATTR_PORT_INDEX'))) dl.close() pyroute2-0.4.21/examples/nla_operators.py0000664000175000017500000000137413123443516020354 0ustar peetpeet00000000000000#!/usr/bin/python from pprint import pprint from pyroute2 import IPRoute from pyroute2 import IPDB from pyroute2.common import uifname # high-level interface ipdb = IPDB() interface = ipdb.create(ifname=uifname(), kind='dummy').\ commit().\ add_ip('172.16.0.1/24').\ add_ip('172.16.0.2/24').\ commit() # low-level interface just to get raw messages ip = IPRoute() a = [x for x in ip.get_addr() if x['index'] == interface['index']] print('\n8<--------------------- left operand') pprint(a[0]) print('\n8<--------------------- right operand') pprint(a[1]) print('\n8<--------------------- complement') pprint(a[0] - a[1]) print('\n8<--------------------- intersection') pprint(a[0] & a[1]) interface.remove().commit() ip.close() ipdb.release() pyroute2-0.4.21/examples/ipdb_precb.py0000664000175000017500000000133013127143031017556 0ustar peetpeet00000000000000''' Example of "pre" callback ''' from pyroute2 import IPDB from pyroute2.common import uifname p0 = uifname() ### # # "Pre" callbacks are executed before the message # gets processed by IPDB, and in synchronous manner. # Normally, you will not need these callbacks, but # they can be useful to perform some hacks # def cb(ipdb, msg, action): if action == 'RTM_NEWLINK': msg['flags'] = 1234 # create IPDB instance ip = IPDB() # register "pre" callback ip.register_callback(cb, mode='pre') # create an interface ip.create(kind='dummy', ifname=p0).commit() # assert flags assert ip.interfaces[p0].flags == 1234 # cleanup ip.interfaces[p0].remove() ip.interfaces[p0].commit() # release Netlink socket ip.release() pyroute2-0.4.21/examples/ipdb_routes.py0000664000175000017500000000103713123443516020017 0ustar peetpeet00000000000000from pyroute2 import IPDB from pyroute2.common import uifname p0 = uifname() ip = IPDB() # create dummy interface to host routes on ip.create(kind='dummy', ifname=p0).\ add_ip('172.16.1.1/24').\ up().\ commit() # create a route with ip.routes.add({'dst': '172.16.0.0/24', 'gateway': '172.16.1.2'}) as r: pass # modify it with ip.routes['172.16.0.0/24'] as r: r.gateway = '172.16.1.3' # cleanup with ip.routes['172.16.0.0/24'] as r: r.remove() ip.interfaces[p0].remove().commit() ip.release() pyroute2-0.4.21/examples/nl80211_interfaces.py0000664000175000017500000000062113127143031020677 0ustar peetpeet00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- # from pyroute2.iwutil import IW iw = IW() for q in iw.get_interfaces_dump(): phyname = 'phy%i' % int(q.get_attr('NL80211_ATTR_WIPHY')) print('%s\t%s\t%s\t%s' % (q.get_attr('NL80211_ATTR_IFINDEX'), phyname, q.get_attr('NL80211_ATTR_IFNAME'), q.get_attr('NL80211_ATTR_MAC'))) iw.close() pyroute2-0.4.21/examples/devlink_monitor.py0000664000175000017500000000012013123443516020673 0ustar peetpeet00000000000000from pyroute2.devlink import DL dl = DL(groups=~0) print(dl.get()) dl.close() pyroute2-0.4.21/examples/nl80211_monitor.py0000664000175000017500000000016113123443516020251 0ustar peetpeet00000000000000from pyroute2 import IW # register IW to get all the messages iw = IW(groups=0xfff) print(iw.get()) iw.close() pyroute2-0.4.21/examples/create_bond.py0000664000175000017500000000162013123443516017743 0ustar peetpeet00000000000000''' Example: python ./examples/create_bond.py Creates bond interface. ''' from pyroute2 import IPDB from pyroute2.common import uifname ip = IPDB() try: # create unique interface names p0 = uifname() p1 = uifname() ms = uifname() # The same scheme works for bridge interfaces too: you # can create a bridge interface and enslave some ports # to it just as below. ip.create(kind='dummy', ifname=p0).commit() ip.create(kind='dummy', ifname=p1).commit() with ip.create(kind='bond', ifname=ms) as i: # enslave two interfaces i.add_port(ip.interfaces[p0]) i.add_port(ip.interfaces[p1]) # make an example more scary: add IPs i.add_ip('10.251.0.1/24') i.add_ip('10.251.0.2/24') finally: for i in (p0, p1, ms): try: ip.interfaces[i].remove().commit() except: pass ip.release() pyroute2-0.4.21/examples/README.md0000664000175000017500000000134513123443516016407 0ustar peetpeet00000000000000Examples ======== This directory contains code examples, written in the form of scripts. An example script, placed here, *must* meet following requirements: * Able to run as a script w/o any command line parameters. * Able to be imported as a module, running the code at import time. * An example script *must* clean up after itself all it created. * All allocated resources *must* be released. * Significant exceptions *must* be raised further, but *after* cleanup. * There *must* be corresponding test case in `tests/test_examples.py`. The goal is to keep examples tested and working with the current code base; to increase code coverage; to drop the dead code on time. Actually, thus examples become a part of the integration testing. pyroute2-0.4.21/examples/ipdb_autobr.py0000664000175000017500000000304713127143031017766 0ustar peetpeet00000000000000''' A sample of usage of IPDB generic callbacks ''' from pyroute2 import IPDB from pyroute2.common import uifname # unique interface names -- for the testing p0 = uifname() br0 = uifname() ### # # The callback receives three arguments: # 1. ipdb reference # 2. msg arrived # 3. action (actually, that's msg['event'] field) # # By default, callbacks are registered as 'post', # it means that they're executed after any other # action on a message arrival. # # More details read in pyroute2.ipdb # def cb(ipdb, msg, action): global p0 global br0 if action == 'RTM_NEWLINK' and \ msg.get_attr('IFLA_IFNAME', '') == p0: # get corresponding interface -- in the case of # post-callbacks it is created already interface = ipdb.interfaces[msg['index']] # add it as a port to the bridge ipdb.interfaces[br0].add_port(interface) try: ipdb.interfaces[br0].commit() except Exception: pass # create IPDB instance with IPDB() as ip: # create watchdogs wd0 = ip.watchdog(ifname=br0) wd1 = ip.watchdog(ifname=p0) # create bridge ip.create(kind='bridge', ifname=br0).commit() # wait the bridge to be created wd0.wait() # register callback cuid = ip.register_callback(cb) # create ports ip.create(kind='dummy', ifname=p0).commit() # sleep for interfaces wd1.wait() ip.unregister_callback(cuid) # cleanup for i in (p0, br0): try: ip.interfaces[i].remove().commit() except: pass pyroute2-0.4.21/examples/dhclient.py0000664000175000017500000000363313123443516017276 0ustar peetpeet00000000000000import sys import select from pprint import pprint from pyroute2.dhcp import BOOTREQUEST from pyroute2.dhcp import DHCPDISCOVER from pyroute2.dhcp import DHCPOFFER from pyroute2.dhcp import DHCPREQUEST from pyroute2.dhcp import DHCPACK from pyroute2.dhcp.dhcp4msg import dhcp4msg from pyroute2.dhcp.dhcp4socket import DHCP4Socket def req(s, poll, msg, expect): do_req = True xid = None while True: # get transaction id if do_req: xid = s.put(msg)['xid'] # wait for response events = poll.poll(2) for (fd, event) in events: response = s.get() if response['xid'] != xid: do_req = False continue if response['options']['message_type'] != expect: raise Exception("DHCP protocol error") return response do_req = True def action(ifname): s = DHCP4Socket(ifname) poll = select.poll() poll.register(s, select.POLLIN | select.POLLPRI) # DISCOVER discover = dhcp4msg({'op': BOOTREQUEST, 'chaddr': s.l2addr, 'options': {'message_type': DHCPDISCOVER, 'parameter_list': [1, 3, 6, 12, 15, 28]}}) reply = req(s, poll, discover, expect=DHCPOFFER) # REQUEST request = dhcp4msg({'op': BOOTREQUEST, 'chaddr': s.l2addr, 'options': {'message_type': DHCPREQUEST, 'requested_ip': reply['yiaddr'], 'server_id': reply['options']['server_id'], 'parameter_list': [1, 3, 6, 12, 15, 28]}}) reply = req(s, poll, request, expect=DHCPACK) pprint(reply) s.close() return reply if __name__ == '__main__': if len(sys.argv) > 1: ifname = sys.argv[1] else: ifname = 'eth0' action(ifname) pyroute2-0.4.21/examples/devlink_list.py0000664000175000017500000000027513123443516020172 0ustar peetpeet00000000000000from pyroute2 import DL dl = DL() for q in dl.get_dump(): print('%s\t%s' % (q.get_attr('DEVLINK_ATTR_BUS_NAME'), q.get_attr('DEVLINK_ATTR_DEV_NAME'))) dl.close() pyroute2-0.4.21/examples/ip_monitor.py0000664000175000017500000000027313123443516017660 0ustar peetpeet00000000000000''' Simplest example to monitor Netlink events with a Python script. ''' from pyroute2 import IPRSocket from pprint import pprint ip = IPRSocket() ip.bind() pprint(ip.get()) ip.close() pyroute2-0.4.21/examples/create_interface.py0000664000175000017500000000135613123443516020767 0ustar peetpeet00000000000000''' Example: python ./examples/create_interface.py Creates dummy interface. ''' from pyroute2 import IPDB from pyroute2.common import uifname ip = IPDB() try: # dummy, bridge and bond interfaces are created in the # same way # # uifname() function is used here only to generate a # unique name of the interface for the regression testing, # you can pick up any name # # details of this restriction are in the documentation # # possible kinds: # * bond # * bridge # * dummy # * vlan -- see /examples/create_vlan.py # dummy = ip.create(kind='dummy', ifname=uifname()) dummy.commit() finally: try: dummy.remove().commit() except: pass ip.release() pyroute2-0.4.21/setup.ini0000664000175000017500000000007313152333361015146 0ustar peetpeet00000000000000[setup] version=0.4 release=0.4.21 setuplib=distutils.core