├── .gitignore ├── .travis.yml ├── CHANGES.rst ├── COPYING ├── MANIFEST.in ├── README.rst ├── man ├── pethtool.8.asciidoc └── pifconfig.8.asciidoc ├── python-ethtool ├── etherinfo.c ├── etherinfo.h ├── etherinfo_obj.c ├── etherinfo_obj.h ├── etherinfo_struct.h ├── ethtool-copy.h ├── ethtool.c ├── include │ └── py3c │ │ └── compat.h ├── netlink-address.c └── netlink.c ├── scripts ├── pethtool └── pifconfig ├── setup.py ├── tests ├── __init__.py ├── parse_ifconfig.py ├── test_ethtool.py └── test_scripts.py └── tox.ini /.gitignore: -------------------------------------------------------------------------------- 1 | .tox/ 2 | build/ 3 | dist/ 4 | *.so 5 | *.pyc 6 | *.pyo 7 | *.egg-info 8 | __pycache__ 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - '2.7' 4 | - '3.5' 5 | - '3.6' 6 | - '3.7' 7 | - '3.8' 8 | - '3.9' 9 | - '3.10-dev' 10 | dist: xenial 11 | sudo: required 12 | addons: 13 | apt: 14 | packages: 15 | - libc6-dev 16 | - libnl-3-dev 17 | - libnl-route-3-dev 18 | cache: 19 | directories: 20 | - $HOME/.cache/pip 21 | - $HOME/virtualenv/python$TRAVIS_PYTHON_VERSION/lib/python$TRAVIS_PYTHON_VERSION/site-packages 22 | - $HOME/virtualenv/python$TRAVIS_PYTHON_VERSION/bin 23 | notifications: 24 | email: false 25 | install: 26 | - pip install --upgrade tox tox-travis 27 | - sudo ip link add dummy1 type dummy 28 | - sudo ip link add dummy4 type dummy 29 | - sudo ip addr add 203.0.113.1 broadcast 203.0.113.255 dev dummy4 30 | - sudo ip addr add 198.51.100.1 broadcast 198.51.100.255 dev dummy4 31 | - sudo ip link set dummy1 up 32 | - sudo ip link set dummy4 up 33 | # Be ready for IPv6 34 | # - sudo ip link add dummy46 type dummy 35 | # - sudo ip addr add 203.0.113.10 broadcast 203.0.113.255 dev dummy46 36 | # - sudo ip addr add 198.51.100.10 broadcast 198.51.100.255 dev dummy46 37 | # - sudo ip addr add 2001:db8::10/64 dev dummy46 38 | # - sudo ip addr add 2001:db9::10/64 dev dummy46 39 | # - sudo ip link add dummy6 type dummy 40 | # - sudo ip addr add 2001:db8::1/64 dev dummy6 41 | # - sudo ip addr add 2001:db9::1/64 dev dummy6 42 | # - sudo ip link set dummy6 up 43 | # - sudo ip link set dummy46 up 44 | script: tox 45 | -------------------------------------------------------------------------------- /CHANGES.rst: -------------------------------------------------------------------------------- 1 | Changelog 2 | ========= 3 | 4 | 0.15 5 | ---- 6 | Thu Jul 29 2021 Lumír Balhar 7 | 8 | - Tests are now skipped for wireguard devices 9 | - pifconfig is now able to show more than one IPv4 address per interface 10 | - pifconfig supports interfaces with no IPv4 address 11 | 12 | 0.14 13 | ---- 14 | Wed Sep 12 2018 Miro Hrončok 15 | 16 | - Declared project as bugfix only from now on 17 | - Support Python 3.7 18 | - Fix important issues reported by static analysis 19 | - Fix installation on non-UTF-8 locales on Python 3.5 and 3.6 20 | - Added set_gso(), get_gro() and set_gro() functions 21 | - Added installation instructions 22 | 23 | 0.13 24 | ---- 25 | Tue Jun 13 2017 Miro Hrončok 26 | 27 | - First release on PyPI 28 | - Supports both Python 2.7 and 3.5+ 29 | - Dropped support for Python 2.6 30 | - Upstream URL changed to https://github.com/fedora-python/python-ethtool 31 | - Introduced a basic README file 32 | - PEP7 and PEP8 (code style) improvements 33 | - Fix compilation errors on modern Fedoras 34 | 35 | 0.12 36 | ---- 37 | Tue Mar 21 2017 Charalampos Stratakis 38 | 39 | - First attempt at python3 support 40 | 41 | 0.11 42 | ---- 43 | Thu May 8 2014 David Sommerseth 44 | 45 | - Improved error handling several places 46 | - Ensure that we get a valid libnl NETLINK connection when connecting 47 | - URL updates to SPEC file 48 | 49 | 0.10 50 | ---- 51 | Fri Jan 10 2014 David Sommerseth 52 | 53 | - Not really a full release, but a preliminary release to get more wide testing 54 | - FSF Copyright updates 55 | - Build fixes 56 | - Mostly code cleanup 57 | 58 | 0.9 59 | --- 60 | Wed Dec 11 2013 David Sommerseth 61 | 62 | - Fixed get_active_devices() for IPv6 only interfaces 63 | - Moved from libnl1 to libnl3 64 | - Refactor PyNetlink*Address implementation 65 | 66 | 0.8 67 | --- 68 | Tue Feb 19 2013 David Malcolm 69 | 70 | - Enable IPv6 in pifethtool example 71 | - Code cleanup, fixing buffer overflows, memory leaks, etc 72 | 73 | 0.7 74 | --- 75 | Mon Apr 11 2011 David Sommerseth 76 | 77 | - Fixed several memory leaks (commit aa2c20e697af, abc7f912f66d) 78 | - Improved error checking towards NULL values(commit 4e928d62a8e3) 79 | - Fixed typo in pethtool --help (commit 710766dc722) 80 | - Only open a NETLINK connection when needed (commit 508ffffbb3c) 81 | - Added man page for pifconfig and pethtool (commit 9f0d17aa532, rhbz#638475) 82 | - Force NETLINK socket to close on fork() using FD_CLOEXEC (commit 1680cbeb40e) 83 | 84 | 0.6 85 | --- 86 | Wed Jan 19 2011 David Sommerseth 87 | 88 | - Don't segfault if we don't receive any address from rtnl_link_get_addr() 89 | - Remove errornous file from MANIFEST 90 | - Added ethtool.version string constant 91 | - Avoid duplicating IPv6 address information 92 | - import sys module in setup.py (Miroslav Suchy) 93 | 94 | 0.5 95 | --- 96 | Mon Aug 9 2010 David Sommerseth 97 | 98 | - Fixed double free issue (commit c52ed2cbdc5b851ebc7b) 99 | 100 | 0.4 101 | --- 102 | Wed Apr 28 2010 David Sommerseth 103 | 104 | - David Sommerseth is now taking over the maintenance of python-ethtool 105 | - New URLs for upstream source code 106 | - Added new API: ethtool.get_interfaces_info() - returns list of etherinfo objects 107 | - Added support retrieving for IPv6 address, using etherinfo::get_ipv6_addresses() 108 | 109 | 0.3 110 | --- 111 | Tue Aug 26 2008 Arnaldo Carvalho de Melo 112 | 113 | - Add get_flags method from the first python-ethtool contributor, yay 114 | - Add pifconfig command, that mimics the ifconfig tool using the 115 | bindings available 116 | 117 | 0.2 118 | --- 119 | Wed Aug 20 2008 Arnaldo Carvalho de Melo 120 | 121 | - Expand description and summary fields, as part of the fedora 122 | review process. 123 | 124 | 0.1 125 | --- 126 | Tue Dec 18 2007 Arnaldo Carvalho de Melo 127 | 128 | - Get ethtool code from rhpl 0.212 129 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include COPYING 2 | include *.rst 3 | recursive-include man *.asciidoc 4 | recursive-include python-ethtool *.h 5 | recursive-include tests *.py 6 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Python ethtool module 2 | ===================== 3 | 4 | *Python bindings for the ethtool kernel interface* 5 | 6 | The Python ``ethtool`` module allows querying and partially controlling network 7 | interfaces, driver, and hardware settings. 8 | 9 | .. warning:: 10 | This is the new upstream for python-ethtool maintained by Fedora's 11 | Python SIG. We ported it to Python 3 and only maintain it for the current 12 | tools to keep working. **No new development is happening. This is a 13 | deprecated package.** If you are considering to start using this, please 14 | don't. We recommend `netifaces `_ instead. Another alternative is `ethtool parsers `_ from `insights-core `_. 15 | 16 | Installation 17 | ------------ 18 | 19 | The easiest way to install ``ethtool`` is to use your distribution packages 20 | repositories. For example: 21 | 22 | **Fedora**: ``sudo dnf install python3-ethtool`` or ``sudo dnf install python2-ethtool`` 23 | 24 | **Ubuntu**: ``sudo apt install python-ethtool`` 25 | 26 | In order to install ``ethtool`` from source or PyPI install its dependencies first: 27 | 28 | **Fedora**: ``sudo dnf install libnl3-devel gcc redhat-rpm-config python3-devel`` 29 | 30 | **Ubuntu**: ``sudo apt install python3 python3-setuptools libpython3.6-dev libnl-route-3-dev`` 31 | 32 | And then install ``ethtool``: 33 | 34 | **from PyPI**: ``pip3 install ethtool`` 35 | 36 | **from source**: ``python3 setup.py install`` 37 | 38 | 39 | Usage 40 | ----- 41 | 42 | ``ethtool`` may be used as a Python library:: 43 | 44 | >>> import ethtool 45 | >>> ethtool.get_active_devices() 46 | ['lo', 'enp0s31f6', 'wlp4s0', 'virbr0', 'docker0', 'virbr1', 'eth0', 'tun0'] 47 | >>> ethtool.get_ipaddr('lo') 48 | '127.0.0.1' 49 | 50 | The ``ethtool`` package also provides the ``pethtool`` and ``pifconfig`` utilities. More example usage may be gathered from their sources, 51 | `pethtool.py `_ 52 | and 53 | `pifconfig.py `_. 54 | 55 | 56 | ``pethtool`` mimics behavior of the ``ethtool`` utility, but does not 57 | support all options. 58 | 59 | e.g., to get driver information on the ``eth0`` interface:: 60 | 61 | $ pethtool -i eth0 62 | driver: cdc_ether 63 | bus-info: usb-0000:00:14.0-4.1.3 64 | 65 | Analogically, ``pifconfig`` mimics ``ifconfig`` in usage. It may be 66 | used to view information on an interface:: 67 | 68 | $ pifconfig lo 69 | lo 70 | inet addr:127.0.0.1 Mask:255.0.0.0 71 | inet6 addr: ::1/128 Scope: host 72 | UP LOOPBACK RUNNING 73 | 74 | 75 | Further usage information may be found in the respective manpages for 76 | `pethtool `_ 77 | and 78 | `pifconfig `_. 79 | 80 | Tests 81 | ----- 82 | 83 | Tests may be run by ``tox``. 84 | 85 | Authors 86 | ------- 87 | 88 | * Andy Grover 89 | * Antoni S. Puimedon 90 | * Arnaldo Carvalho de Melo 91 | * Bohuslav Kabrda 92 | * Braňo Náter 93 | * Dave Malcolm 94 | * David S. Miller 95 | * David Sommerseth 96 | * Harald Hoyer 97 | * Charalampos Stratakis 98 | * Jeff Garzik 99 | * Lumir Balhar 100 | * Miro Hrončok 101 | * Miroslav Suchý 102 | * Ruben Kerkhof 103 | * Sanqui 104 | * Yaakov Selkowitz 105 | 106 | Current maintainers 107 | ------------------- 108 | 109 | * Lumír Balhar 110 | * Miro Hrončok 111 | * Charalampos Stratakis 112 | 113 | Contributing 114 | ------------ 115 | 116 | Feel free to help us with improving test coverage, porting to Python 3 117 | or anything else. 118 | Issues and PRs `on GitHub `_ 119 | are welcome. 120 | 121 | License 122 | ------- 123 | 124 | The Python ``ethtool`` project is free software distributed under the terms of 125 | the GNU General Public License v2.0, see 126 | `COPYING `_. 127 | 128 | -------------------------------------------------------------------------------- /man/pethtool.8.asciidoc: -------------------------------------------------------------------------------- 1 | pethtool(8) 2 | =========== 3 | 4 | NAME 5 | ---- 6 | pethtool - Display or change ethernet card settings 7 | 8 | 9 | SYNOPSIS 10 | -------- 11 | pethtool [OPTIONS] [] 12 | 13 | 14 | DESCRIPTION 15 | ----------- 16 | 17 | This script mimic ethtool behavior, but is written purely in python 18 | and python module ethtool. 19 | 20 | 21 | OPTIONS 22 | ------- 23 | 24 | INTERFACE:: 25 | Is the name of the ethernet device on which pethtool should operate. 26 | 27 | -h, --help:: 28 | Show help message and exit. 29 | 30 | -c|--show-coalesce:: 31 | Show coalesce options 32 | 33 | -C|--coalesce:: 34 | Set coalesce options 35 | 36 | adaptive-rx on|off 37 | adaptive-tx on|off 38 | rx-usecs N 39 | rx-frames N 40 | rx-usecs-irq N 41 | rx-frames-irq N 42 | tx-usecs N 43 | tx-frames N 44 | tx-usecs-irq N 45 | tx-frames-irq N 46 | stats-block-usecs N 47 | pkt-rate-low N 48 | rx-usecs-low N 49 | rx-frames-low N 50 | tx-usecs-low N 51 | tx-frames-low N 52 | pkt-rate-high N 53 | rx-usecs-high N 54 | rx-frames-high N 55 | tx-usecs-high N 56 | tx-frames-high N 57 | sample-interval N 58 | 59 | 60 | -i|--driver:: 61 | Show driver information 62 | 63 | -k|--show-offload:: 64 | Get protocol offload information 65 | 66 | -K|--offload:: 67 | Set protocol offload;; 68 | [ tso on|off ] 69 | 70 | 71 | 72 | SEE ALSO 73 | -------- 74 | ethtool(8) 75 | 76 | 77 | AUTHORS 78 | ------- 79 | Arnaldo Carvalho de Melo 80 | 81 | Man page written by Miroslav Suchý 82 | -------------------------------------------------------------------------------- /man/pifconfig.8.asciidoc: -------------------------------------------------------------------------------- 1 | pifconfig(8) 2 | ============ 3 | 4 | NAME 5 | ---- 6 | pifconfig - display information about a network interface 7 | 8 | 9 | SYNOPSIS 10 | -------- 11 | pifconfig [INTERFACE [INTERFACE [INTERFACE] ...]] 12 | 13 | 14 | DESCRIPTION 15 | ----------- 16 | 17 | This script mimic ifconfig behavior, but is written purely in python 18 | and python module ethtool. 19 | 20 | 21 | OPTIONS 22 | ------- 23 | 24 | INTERFACE:: 25 | Display information about only the listed interfaces. If no interface is given 26 | all interfaces are displayed. 27 | 28 | -h, --help:: 29 | Show help message and exit. 30 | 31 | 32 | SEE ALSO 33 | -------- 34 | ifconfig(8) 35 | 36 | 37 | 38 | AUTHORS 39 | ------- 40 | Arnaldo Carvalho de Melo 41 | 42 | Man page written by Miroslav Suchý , David Sommerseth 43 | -------------------------------------------------------------------------------- /python-ethtool/etherinfo.c: -------------------------------------------------------------------------------- 1 | /* etherinfo.c - Retrieve ethernet interface info via NETLINK 2 | * 3 | * Copyright (C) 2009-2013 Red Hat Inc. 4 | * 5 | * David Sommerseth 6 | * Parts of this code is based on ideas and solutions in iproute2 7 | * 8 | * This application is free software; you can redistribute it and/or modify it 9 | * under the terms of the GNU General Public License as published by the Free 10 | * Software Foundation; version 2. 11 | * 12 | * This application is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * General Public License for more details. 16 | */ 17 | 18 | #include 19 | #include "include/py3c/compat.h" 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include "etherinfo_struct.h" 39 | #include "etherinfo.h" 40 | 41 | /* 42 | * 43 | * Internal functions for working with struct etherinfo 44 | * 45 | */ 46 | 47 | 48 | /** 49 | * libnl callback function. 50 | * Does the real parsing of a record returned by NETLINK. 51 | * This function parses LINK related packets 52 | * 53 | * @param obj Pointer to a struct nl_object response 54 | * @param arg Pointer to a struct etherinfo element where the parse result 55 | * will be saved 56 | */ 57 | static void callback_nl_link(struct nl_object *obj, void *arg) 58 | { 59 | PyEtherInfo *ethi = (PyEtherInfo *) arg; 60 | struct rtnl_link *link = (struct rtnl_link *) obj; 61 | char hwaddr[130]; 62 | 63 | if ((ethi == NULL) || (ethi->hwaddress != NULL)) { 64 | return; 65 | } 66 | 67 | memset(&hwaddr, 0, 130); 68 | nl_addr2str(rtnl_link_get_addr(link), hwaddr, sizeof(hwaddr)); 69 | if (ethi->hwaddress) { 70 | Py_XDECREF(ethi->hwaddress); 71 | } 72 | ethi->hwaddress = PyStr_FromFormat("%s", hwaddr); 73 | } 74 | 75 | 76 | /** 77 | * libnl callback function. Does the real parsing of a record returned by 78 | * NETLINK. This function parses ADDRESS related packets 79 | * 80 | * @param obj Pointer to a struct nl_object response 81 | * @param arg Pointer to a struct etherinfo element where the parse result 82 | * will be saved 83 | */ 84 | static void callback_nl_address(struct nl_object *obj, void *arg) 85 | { 86 | PyObject *py_addrlist = (PyObject *) arg; 87 | struct rtnl_addr *rtaddr = (struct rtnl_addr *) obj; 88 | PyObject *addr_obj = NULL; 89 | int af_family = -1; 90 | 91 | if (py_addrlist == NULL) { 92 | return; 93 | } 94 | 95 | /* Ensure that we're processing only known address types. 96 | * Currently only IPv4 and IPv6 is handled 97 | */ 98 | af_family = rtnl_addr_get_family(rtaddr); 99 | if (af_family != AF_INET && af_family != AF_INET6) { 100 | return; 101 | } 102 | 103 | /* Prepare a new Python object with the IP address */ 104 | addr_obj = make_python_address_from_rtnl_addr(rtaddr); 105 | if (!addr_obj) { 106 | return; 107 | } 108 | /* Append the IP address object to the address list */ 109 | PyList_Append(py_addrlist, addr_obj); 110 | Py_DECREF(addr_obj); 111 | } 112 | 113 | 114 | /** 115 | * Sets the etherinfo.index member to the corresponding device set in 116 | * etherinfo.device 117 | * 118 | * @param self A pointer the current PyEtherInfo Python object which contains 119 | * the device name and the place where to save the corresponding 120 | * index value. 121 | * 122 | * @return Returns 1 on success, otherwise 0. On error, a Python error 123 | * exception is set. 124 | */ 125 | static int _set_device_index(PyEtherInfo *self) 126 | { 127 | struct nl_cache *link_cache; 128 | struct rtnl_link *link; 129 | 130 | /* Find the interface index we're looking up. 131 | * As we don't expect it to change, we're reusing a "cached" 132 | * interface index if we have that 133 | */ 134 | if (self->index < 0) { 135 | if ((errno = rtnl_link_alloc_cache(get_nlc(), 136 | AF_UNSPEC, &link_cache)) < 0) { 137 | PyErr_SetString(PyExc_OSError, nl_geterror(errno)); 138 | return 0; 139 | } 140 | link = rtnl_link_get_by_name(link_cache, PyStr_AsString(self->device)); 141 | if (!link) { 142 | errno = ENODEV; 143 | PyErr_SetFromErrno(PyExc_IOError); 144 | nl_cache_free(link_cache); 145 | return 0; 146 | } 147 | self->index = rtnl_link_get_ifindex(link); 148 | if (self->index <= 0) { 149 | errno = ENODEV; 150 | PyErr_SetFromErrno(PyExc_IOError); 151 | rtnl_link_put(link); 152 | nl_cache_free(link_cache); 153 | return 0; 154 | } 155 | 156 | rtnl_link_put(link); 157 | nl_cache_free(link_cache); 158 | } 159 | return 1; 160 | } 161 | 162 | 163 | /* 164 | * 165 | * Exported functions - API frontend 166 | * 167 | */ 168 | 169 | /** 170 | * Populate the PyEtherInfo Python object with link information for the current 171 | * device 172 | * 173 | * @param self Pointer to the device object, a PyEtherInfo Python object 174 | * 175 | * @return Returns 1 on success, otherwise 0 176 | */ 177 | int get_etherinfo_link(PyEtherInfo *self) 178 | { 179 | struct nl_cache *link_cache; 180 | struct rtnl_link *link; 181 | int err = 0; 182 | 183 | if (!self) { 184 | return 0; 185 | } 186 | 187 | /* Open a NETLINK connection on-the-fly */ 188 | if (!open_netlink(self)) { 189 | PyErr_Format(PyExc_RuntimeError, 190 | "Could not open a NETLINK connection for %s", 191 | PyStr_AsString(self->device)); 192 | return 0; 193 | } 194 | 195 | if (_set_device_index(self) != 1) { 196 | return 0; 197 | } 198 | 199 | /* Extract MAC/hardware address of the interface */ 200 | if ((err = rtnl_link_alloc_cache(get_nlc(), AF_UNSPEC, &link_cache)) < 0) { 201 | PyErr_SetString(PyExc_OSError, nl_geterror(err)); 202 | return 0; 203 | } 204 | link = rtnl_link_alloc(); 205 | if (!link) { 206 | errno = ENOMEM; 207 | PyErr_SetFromErrno(PyExc_OSError); 208 | return 0; 209 | } 210 | rtnl_link_set_ifindex(link, self->index); 211 | nl_cache_foreach_filter(link_cache, OBJ_CAST(link), 212 | callback_nl_link, self); 213 | rtnl_link_put(link); 214 | nl_cache_free(link_cache); 215 | 216 | return 1; 217 | } 218 | 219 | 220 | 221 | /** 222 | * Query NETLINK for device IP address configuration 223 | * 224 | * @param self A PyEtherInfo Python object for the current device to retrieve 225 | * IP address configuration data from 226 | * @param query What to query for. Must be NLQRY_ADDR4 for IPv4 addresses or 227 | * NLQRY_ADDR6 for IPv6 addresses. 228 | * 229 | * @return Returns a Python list containing PyNetlinkIPaddress objects on 230 | * success, otherwise NULL 231 | */ 232 | PyObject * get_etherinfo_address(PyEtherInfo *self, nlQuery query) 233 | { 234 | struct nl_cache *addr_cache; 235 | struct rtnl_addr *addr; 236 | PyObject *addrlist = NULL; 237 | int err = 0; 238 | 239 | if (!self) { 240 | return NULL; 241 | } 242 | 243 | /* Open a NETLINK connection on-the-fly */ 244 | if (!open_netlink(self)) { 245 | PyErr_Format(PyExc_RuntimeError, 246 | "Could not open a NETLINK connection for %s", 247 | PyStr_AsString(self->device)); 248 | return NULL; 249 | } 250 | 251 | if(!_set_device_index(self)) { 252 | return NULL; 253 | } 254 | 255 | /* Query the for requested info via NETLINK */ 256 | /* Extract IP address information */ 257 | if ((err = rtnl_addr_alloc_cache(get_nlc(), &addr_cache)) < 0) { 258 | PyErr_SetString(PyExc_OSError, nl_geterror(err)); 259 | nl_cache_free(addr_cache); 260 | return NULL; 261 | } 262 | 263 | addr = rtnl_addr_alloc(); 264 | 265 | if (!addr) { 266 | errno = ENOMEM; 267 | PyErr_SetFromErrno(PyExc_OSError); 268 | return NULL; 269 | } 270 | rtnl_addr_set_ifindex(addr, self->index); 271 | 272 | switch( query) { 273 | case NLQRY_ADDR4: 274 | rtnl_addr_set_family(addr, AF_INET); 275 | break; 276 | 277 | case NLQRY_ADDR6: 278 | rtnl_addr_set_family(addr, AF_INET6); 279 | break; 280 | 281 | default: 282 | return NULL; 283 | } 284 | 285 | /* Retrieve all address information */ 286 | addrlist = PyList_New(0); /* The list where to put the address object */ 287 | assert(addrlist); 288 | 289 | /* Loop through all configured addresses */ 290 | nl_cache_foreach_filter(addr_cache, OBJ_CAST(addr), 291 | callback_nl_address, addrlist); 292 | rtnl_addr_put(addr); 293 | nl_cache_free(addr_cache); 294 | 295 | return addrlist; 296 | } 297 | -------------------------------------------------------------------------------- /python-ethtool/etherinfo.h: -------------------------------------------------------------------------------- 1 | /* etherinfo.h - Retrieve ethernet interface info via NETLINK 2 | * 3 | * Copyright (C) 2009-2011 Red Hat Inc. 4 | * 5 | * David Sommerseth 6 | * 7 | * This application is free software; you can redistribute it and/or modify it 8 | * under the terms of the GNU General Public License as published by the Free 9 | * Software Foundation; version 2. 10 | * 11 | * This application is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * General Public License for more details. 15 | */ 16 | 17 | #ifndef _ETHERINFO_H 18 | #define _ETHERINFO_H 19 | 20 | /** Supported query types in the etherinfo code */ 21 | typedef enum {NLQRY_ADDR4, NLQRY_ADDR6} nlQuery; 22 | 23 | int get_etherinfo_link(PyEtherInfo *data); 24 | PyObject * get_etherinfo_address(PyEtherInfo *self, nlQuery query); 25 | 26 | int open_netlink(PyEtherInfo *); 27 | struct nl_sock * get_nlc(); 28 | void close_netlink(PyEtherInfo *); 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /python-ethtool/etherinfo_obj.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009-2013 Red Hat Inc. 3 | * 4 | * David Sommerseth 5 | * 6 | * This application is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU General Public License as published by the Free 8 | * Software Foundation; version 2. 9 | * 10 | * This application is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * General Public License for more details. 14 | */ 15 | 16 | 17 | /** 18 | * @file etherinfo_obj.c 19 | * @author David Sommerseth 20 | * @date Fri Sep 4 18:41:28 2009 21 | * 22 | * @brief Python ethtool.etherinfo class functions. 23 | * 24 | */ 25 | 26 | #include 27 | #include "include/py3c/compat.h" 28 | #include 29 | #include "structmember.h" 30 | 31 | #include 32 | #include 33 | #include "etherinfo_struct.h" 34 | #include "etherinfo.h" 35 | 36 | /** 37 | * ethtool.etherinfo deallocator - cleans up when a object is deleted 38 | * 39 | * @param self PyEtherInfo Python object to deallocate 40 | */ 41 | static void _ethtool_etherinfo_dealloc(PyEtherInfo *self) 42 | { 43 | close_netlink(self); 44 | Py_XDECREF(self->device); 45 | self->device = NULL; 46 | Py_XDECREF(self->hwaddress); 47 | self->hwaddress = NULL; 48 | Py_TYPE(self)->tp_free((PyObject*)self); 49 | } 50 | 51 | 52 | /* 53 | The old approach of having a single IPv4 address per device meant each result 54 | that came in from netlink overwrote the old result. 55 | 56 | Mimic it by returning the last entry in the list (if any). 57 | 58 | The return value is a *borrowed reference* (or NULL) 59 | */ 60 | static PyNetlinkIPaddress * get_last_ipv4_address(PyObject *addrlist) 61 | { 62 | Py_ssize_t size; 63 | 64 | if (!addrlist) { 65 | return NULL; 66 | } 67 | 68 | if (!PyList_Check(addrlist)) { 69 | return NULL; 70 | } 71 | 72 | size = PyList_Size(addrlist); 73 | if (size > 0) { 74 | PyNetlinkIPaddress *item = (PyNetlinkIPaddress *) 75 | PyList_GetItem(addrlist, size - 1); 76 | if (Py_TYPE(item) == ðtool_netlink_ip_address_Type) { 77 | return item; 78 | } 79 | } 80 | 81 | return NULL; 82 | } 83 | 84 | /** 85 | * Creates a human readable format of the information when object is being 86 | * treated as a string 87 | * 88 | * @param self Pointer to the current PyEtherInfo device object 89 | * 90 | * @return Returns a PyObject with a string with all of the information 91 | */ 92 | PyObject *_ethtool_etherinfo_str(PyEtherInfo *self) 93 | { 94 | PyObject *ret = NULL; 95 | PyObject *ipv4addrs = NULL, *ipv6addrs = NULL; 96 | 97 | if (!self) { 98 | PyErr_SetString(PyExc_AttributeError, "No data available"); 99 | return NULL; 100 | } 101 | 102 | get_etherinfo_link(self); 103 | 104 | ret = PyStr_FromFormat("Device %s:\n", PyStr_AsString(self->device)); 105 | 106 | if (self->hwaddress) { 107 | ret = PyStr_Concat(ret, 108 | PyStr_FromFormat("\tMAC address: %s\n", 109 | PyStr_AsString(self->hwaddress))); 110 | } 111 | 112 | ipv4addrs = get_etherinfo_address(self, NLQRY_ADDR4); 113 | if (ipv4addrs) { 114 | Py_ssize_t i; 115 | for (i = 0; i < PyList_Size(ipv4addrs); i++) { 116 | PyNetlinkIPaddress *py_addr = (PyNetlinkIPaddress *) 117 | PyList_GetItem(ipv4addrs, i); 118 | PyObject *tmp = PyStr_FromFormat("\tIPv4 address: "); 119 | tmp = PyStr_Concat(tmp, py_addr->local); 120 | tmp = PyStr_Concat(tmp, 121 | PyStr_FromFormat("/%d", py_addr->prefixlen)); 122 | if (py_addr->ipv4_broadcast) { 123 | tmp = PyStr_Concat( 124 | tmp, PyStr_FromFormat("\tBroadcast: %s\n", 125 | PyStr_AsString( 126 | py_addr->ipv4_broadcast))); 127 | } else { 128 | tmp = PyStr_Concat(tmp, PyStr_FromFormat("\n")); 129 | } 130 | 131 | ret = PyStr_Concat(ret, tmp); 132 | } 133 | } 134 | 135 | ipv6addrs = get_etherinfo_address(self, NLQRY_ADDR6); 136 | if (ipv6addrs) { 137 | Py_ssize_t i; 138 | for (i = 0; i < PyList_Size(ipv6addrs); i++) { 139 | PyNetlinkIPaddress *py_addr = (PyNetlinkIPaddress *) 140 | PyList_GetItem(ipv6addrs, i); 141 | PyObject *tmp = PyStr_FromFormat("\tIPv6 address: [%s] %s/%d\n", 142 | PyStr_AsString(py_addr->scope), 143 | PyStr_AsString(py_addr->local), 144 | py_addr->prefixlen); 145 | ret = PyStr_Concat(ret, tmp); 146 | } 147 | } 148 | 149 | return ret; 150 | } 151 | 152 | /** 153 | * Returns a tuple list of configured IPv4 addresses 154 | * 155 | * @param self Pointer to the current PyEtherInfo device object to extract 156 | * IPv4 info from 157 | * @param notused 158 | * 159 | * @return Returns a Python tuple list of NetlinkIP4Address objects 160 | */ 161 | static PyObject *_ethtool_etherinfo_get_ipv4_addresses(PyEtherInfo *self, 162 | PyObject *notused) { 163 | if (!self) { 164 | PyErr_SetString(PyExc_AttributeError, "No data available"); 165 | return NULL; 166 | } 167 | 168 | return get_etherinfo_address(self, NLQRY_ADDR4); 169 | } 170 | 171 | 172 | /** 173 | * Returns a tuple list of configured IPv6 addresses 174 | * 175 | * @param self Pointer to the current PyEtherInfo device object to extract 176 | * IPv6 info from 177 | * @param notused 178 | * 179 | * @return Returns a Python tuple list of NetlinkIP6Address objects 180 | */ 181 | static PyObject *_ethtool_etherinfo_get_ipv6_addresses(PyEtherInfo *self, 182 | PyObject *notused) { 183 | if (!self) { 184 | PyErr_SetString(PyExc_AttributeError, "No data available"); 185 | return NULL; 186 | } 187 | 188 | return get_etherinfo_address(self, NLQRY_ADDR6); 189 | } 190 | 191 | 192 | /** 193 | * Defines all available methods in the ethtool.etherinfo class 194 | * 195 | */ 196 | static PyMethodDef _ethtool_etherinfo_methods[] = { 197 | { "get_ipv4_addresses", 198 | (PyCFunction)_ethtool_etherinfo_get_ipv4_addresses, METH_NOARGS, 199 | "Retrieve configured IPv4 addresses. " 200 | "Returns a list of NetlinkIPaddress objects" 201 | }, 202 | { "get_ipv6_addresses", 203 | (PyCFunction)_ethtool_etherinfo_get_ipv6_addresses, METH_NOARGS, 204 | "Retrieve configured IPv6 addresses. " 205 | "Returns a list of NetlinkIPaddress objects" 206 | }, 207 | {NULL} /**< No methods defined */ 208 | }; 209 | 210 | static PyObject *get_device(PyObject *obj, void *info) 211 | { 212 | PyEtherInfo *self = (PyEtherInfo *) obj; 213 | 214 | if (self->device) { 215 | Py_INCREF(self->device); 216 | return self->device; 217 | } 218 | Py_RETURN_NONE; 219 | } 220 | 221 | static PyObject *get_mac_addr(PyObject *obj, void *info) 222 | { 223 | PyEtherInfo *self = (PyEtherInfo *) obj; 224 | 225 | get_etherinfo_link(self); 226 | if (self->hwaddress) { 227 | Py_INCREF(self->hwaddress); 228 | } 229 | return self->hwaddress; 230 | } 231 | 232 | static PyObject *get_ipv4_addr(PyObject *obj, void *info) 233 | { 234 | PyEtherInfo *self = (PyEtherInfo *) obj; 235 | PyObject *addrlist; 236 | PyNetlinkIPaddress *py_addr; 237 | 238 | addrlist = get_etherinfo_address(self, NLQRY_ADDR4); 239 | /* For compatiblity with old approach, return last IPv4 address: */ 240 | py_addr = get_last_ipv4_address(addrlist); 241 | if (py_addr) { 242 | if (py_addr->local) { 243 | Py_INCREF(py_addr->local); 244 | return py_addr->local; 245 | } 246 | } 247 | 248 | if (PyErr_Occurred()) { 249 | return NULL; 250 | } else { 251 | Py_RETURN_NONE; 252 | } 253 | } 254 | 255 | static PyObject *get_ipv4_mask(PyObject *obj, void *info) 256 | { 257 | PyEtherInfo *self = (PyEtherInfo *) obj; 258 | PyObject *addrlist; 259 | PyNetlinkIPaddress *py_addr; 260 | 261 | addrlist = get_etherinfo_address(self, NLQRY_ADDR4); 262 | py_addr = get_last_ipv4_address(addrlist); 263 | if (py_addr) { 264 | return PyInt_FromLong(py_addr->prefixlen); 265 | } 266 | 267 | if (PyErr_Occurred()) { 268 | return NULL; 269 | } else { 270 | return PyInt_FromLong(0); 271 | } 272 | } 273 | 274 | static PyObject *get_ipv4_bcast(PyObject *obj, void *info) 275 | { 276 | PyEtherInfo *self = (PyEtherInfo *) obj; 277 | PyObject *addrlist; 278 | PyNetlinkIPaddress *py_addr; 279 | 280 | addrlist = get_etherinfo_address(self, NLQRY_ADDR4); 281 | py_addr = get_last_ipv4_address(addrlist); 282 | if (py_addr) { 283 | if (py_addr->ipv4_broadcast) { 284 | Py_INCREF(py_addr->ipv4_broadcast); 285 | return py_addr->ipv4_broadcast; 286 | } 287 | } 288 | 289 | if (PyErr_Occurred()) { 290 | return NULL; 291 | } else { 292 | return PyStr_FromString("0.0.0.0"); 293 | } 294 | } 295 | 296 | 297 | static PyGetSetDef _ethtool_etherinfo_attributes[] = { 298 | {"device", get_device, NULL, "device", NULL}, 299 | {"mac_address", get_mac_addr, NULL, "MAC address", NULL}, 300 | {"ipv4_address", get_ipv4_addr, NULL, "IPv4 address", NULL}, 301 | {"ipv4_netmask", get_ipv4_mask, NULL, "IPv4 netmask", NULL}, 302 | {"ipv4_broadcast", get_ipv4_bcast, NULL, "IPv4 broadcast", NULL}, 303 | {NULL}, 304 | }; 305 | 306 | 307 | /** 308 | * Definition of the functions a Python class/object requires. 309 | * 310 | */ 311 | PyTypeObject PyEtherInfo_Type = { 312 | PyVarObject_HEAD_INIT(NULL, 0) 313 | .tp_name = "ethtool.etherinfo", 314 | .tp_basicsize = sizeof(PyEtherInfo), 315 | .tp_flags = Py_TPFLAGS_DEFAULT, 316 | .tp_dealloc = (destructor)_ethtool_etherinfo_dealloc, 317 | .tp_str = (reprfunc)_ethtool_etherinfo_str, 318 | .tp_getset = _ethtool_etherinfo_attributes, 319 | .tp_methods = _ethtool_etherinfo_methods, 320 | .tp_doc = "Contains information about a specific ethernet device" 321 | }; 322 | -------------------------------------------------------------------------------- /python-ethtool/etherinfo_obj.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009-2010 Red Hat Inc. 3 | * 4 | * David Sommerseth 5 | * 6 | * This application is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU General Public License as published by the Free 8 | * Software Foundation; version 2. 9 | * 10 | * This application is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * General Public License for more details. 14 | */ 15 | 16 | /** 17 | * @file etherinfo_obj.c 18 | * @author David Sommerseth 19 | * @date Fri Sep 4 18:41:28 2009 20 | * 21 | * @brief Python ethtool.etherinfo class functions (header file). 22 | * 23 | */ 24 | 25 | #ifndef __ETHERINFO_OBJ_H 26 | #define __ETHERINFO_OBJ_H 27 | 28 | #include 29 | #include "etherinfo_struct.h" 30 | 31 | void _ethtool_etherinfo_dealloc(PyEtherInfo *); 32 | PyObject *_ethtool_etherinfo_new(PyTypeObject *, PyObject *, PyObject *); 33 | int _ethtool_etherinfo_init(PyEtherInfo *, PyObject *, PyObject *); 34 | PyObject *_ethtool_etherinfo_getter(PyEtherInfo *, PyObject *); 35 | int _ethtool_etherinfo_setter(PyEtherInfo *, PyObject *, PyObject *); 36 | PyObject *_ethtool_etherinfo_str(PyEtherInfo *self); 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /python-ethtool/etherinfo_struct.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009-2013 Red Hat Inc. 3 | * 4 | * David Sommerseth 5 | * 6 | * This application is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU General Public License as published by the Free 8 | * Software Foundation; version 2. 9 | * 10 | * This application is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * General Public License for more details. 14 | */ 15 | 16 | 17 | /** 18 | * @file etherinfo_struct.h 19 | * @author David Sommerseth 20 | * @date Fri Sep 4 19:06:06 2009 21 | * 22 | * @brief Contains the internal ethtool.etherinfo data structure 23 | * 24 | */ 25 | 26 | #ifndef _ETHERINFO_STRUCT_H 27 | #define _ETHERINFO_STRUCT_H 28 | 29 | #include 30 | 31 | /* Python object containing data baked from a (struct rtnl_addr) */ 32 | typedef struct PyNetlinkIPaddress { 33 | PyObject_HEAD 34 | int family; /**< int: must be AF_INET or AF_INET6 */ 35 | PyObject *local; /**< string: Configured local IP address */ 36 | PyObject *peer; /**< string: Configured peer IP address */ 37 | PyObject *ipv4_broadcast; /**< string: Configured IPv4 broadcast add. */ 38 | int prefixlen; /**< int: Configured network prefix (netmask) */ 39 | PyObject *scope; /**< string: IP address scope */ 40 | } PyNetlinkIPaddress; 41 | extern PyTypeObject ethtool_netlink_ip_address_Type; 42 | 43 | /** 44 | * The Python object containing information about a single interface 45 | * 46 | */ 47 | typedef struct { 48 | PyObject_HEAD 49 | PyObject *device; /**< Device name */ 50 | int index; /**< NETLINK index reference */ 51 | PyObject *hwaddress; /**< string: HW address / MAC address of device */ 52 | unsigned short nlc_active; /**< Is this instance using NETLINK? */ 53 | } PyEtherInfo; 54 | 55 | 56 | 57 | PyObject * make_python_address_from_rtnl_addr(struct rtnl_addr *addr); 58 | 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /python-ethtool/ethtool-copy.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ethtool.h: Defines for Linux ethtool. 3 | * 4 | * Copyright (C) 1998 David S. Miller (davem@redhat.com) 5 | * Copyright 2001 Jeff Garzik 6 | * Portions Copyright 2001 Sun Microsystems (thockin@sun.com) 7 | * Portions Copyright 2002 Intel (eli.kupermann@intel.com, 8 | * christopher.leech@intel.com, 9 | * scott.feldman@intel.com) 10 | */ 11 | 12 | #ifndef _LINUX_ETHTOOL_H 13 | #define _LINUX_ETHTOOL_H 14 | 15 | #ifndef __unused 16 | #define __unused __attribute__ ((unused)) 17 | #endif 18 | 19 | /* This should work for both 32 and 64 bit userland. */ 20 | struct ethtool_cmd { 21 | u32 cmd; 22 | u32 supported; /* Features this interface supports */ 23 | u32 advertising; /* Features this interface advertises */ 24 | u16 speed; /* The forced speed, 10Mb, 100Mb, gigabit */ 25 | u8 duplex; /* Duplex, half or full */ 26 | u8 port; /* Which connector port */ 27 | u8 phy_address; 28 | u8 transceiver; /* Which tranceiver to use */ 29 | u8 autoneg; /* Enable or disable autonegotiation */ 30 | u32 maxtxpkt; /* Tx pkts before generating tx int */ 31 | u32 maxrxpkt; /* Rx pkts before generating rx int */ 32 | u32 reserved[4]; 33 | }; 34 | 35 | #define ETHTOOL_BUSINFO_LEN 32 36 | /* these strings are set to whatever the driver author decides... */ 37 | struct ethtool_drvinfo { 38 | u32 cmd; 39 | char driver[32]; /* driver short name, "tulip", "eepro100" */ 40 | char version[32]; /* driver version string */ 41 | char fw_version[32]; /* firmware version string, if applicable */ 42 | char bus_info[ETHTOOL_BUSINFO_LEN]; /* Bus info for this IF. */ 43 | /* For PCI devices, use pci_dev->slot_name. */ 44 | char reserved1[32]; 45 | char reserved2[16]; 46 | u32 n_stats; /* number of u64's from ETHTOOL_GSTATS */ 47 | u32 testinfo_len; 48 | u32 eedump_len; /* Size of data from ETHTOOL_GEEPROM (bytes) */ 49 | u32 regdump_len; /* Size of data from ETHTOOL_GREGS (bytes) */ 50 | }; 51 | 52 | #define SOPASS_MAX 6 53 | /* wake-on-lan settings */ 54 | struct ethtool_wolinfo { 55 | u32 cmd; 56 | u32 supported; 57 | u32 wolopts; 58 | u8 sopass[SOPASS_MAX]; /* SecureOn(tm) password */ 59 | }; 60 | 61 | /* for passing single values */ 62 | struct ethtool_value { 63 | u32 cmd; 64 | u32 data; 65 | }; 66 | 67 | /* for passing big chunks of data */ 68 | struct ethtool_regs { 69 | u32 cmd; 70 | u32 version; /* driver-specific, indicates different chips/revs */ 71 | u32 len; /* bytes */ 72 | u8 data[0]; 73 | }; 74 | 75 | /* for passing EEPROM chunks */ 76 | struct ethtool_eeprom { 77 | u32 cmd; 78 | u32 magic; 79 | u32 offset; /* in bytes */ 80 | u32 len; /* in bytes */ 81 | u8 data[0]; 82 | }; 83 | 84 | /* for configuring coalescing parameters of chip */ 85 | struct ethtool_coalesce { 86 | u32 cmd; /* ETHTOOL_{G,S}COALESCE */ 87 | 88 | /* How many usecs to delay an RX interrupt after 89 | * a packet arrives. If 0, only rx_max_coalesced_frames 90 | * is used. 91 | */ 92 | u32 rx_coalesce_usecs; 93 | 94 | /* How many packets to delay an RX interrupt after 95 | * a packet arrives. If 0, only rx_coalesce_usecs is 96 | * used. It is illegal to set both usecs and max frames 97 | * to zero as this would cause RX interrupts to never be 98 | * generated. 99 | */ 100 | u32 rx_max_coalesced_frames; 101 | 102 | /* Same as above two parameters, except that these values 103 | * apply while an IRQ is being services by the host. Not 104 | * all cards support this feature and the values are ignored 105 | * in that case. 106 | */ 107 | u32 rx_coalesce_usecs_irq; 108 | u32 rx_max_coalesced_frames_irq; 109 | 110 | /* How many usecs to delay a TX interrupt after 111 | * a packet is sent. If 0, only tx_max_coalesced_frames 112 | * is used. 113 | */ 114 | u32 tx_coalesce_usecs; 115 | 116 | /* How many packets to delay a TX interrupt after 117 | * a packet is sent. If 0, only tx_coalesce_usecs is 118 | * used. It is illegal to set both usecs and max frames 119 | * to zero as this would cause TX interrupts to never be 120 | * generated. 121 | */ 122 | u32 tx_max_coalesced_frames; 123 | 124 | /* Same as above two parameters, except that these values 125 | * apply while an IRQ is being services by the host. Not 126 | * all cards support this feature and the values are ignored 127 | * in that case. 128 | */ 129 | u32 tx_coalesce_usecs_irq; 130 | u32 tx_max_coalesced_frames_irq; 131 | 132 | /* How many usecs to delay in-memory statistics 133 | * block updates. Some drivers do not have an in-memory 134 | * statistic block, and in such cases this value is ignored. 135 | * This value must not be zero. 136 | */ 137 | u32 stats_block_coalesce_usecs; 138 | 139 | /* Adaptive RX/TX coalescing is an algorithm implemented by 140 | * some drivers to improve latency under low packet rates and 141 | * improve throughput under high packet rates. Some drivers 142 | * only implement one of RX or TX adaptive coalescing. Anything 143 | * not implemented by the driver causes these values to be 144 | * silently ignored. 145 | */ 146 | u32 use_adaptive_rx_coalesce; 147 | u32 use_adaptive_tx_coalesce; 148 | 149 | /* When the packet rate (measured in packets per second) 150 | * is below pkt_rate_low, the {rx,tx}_*_low parameters are 151 | * used. 152 | */ 153 | u32 pkt_rate_low; 154 | u32 rx_coalesce_usecs_low; 155 | u32 rx_max_coalesced_frames_low; 156 | u32 tx_coalesce_usecs_low; 157 | u32 tx_max_coalesced_frames_low; 158 | 159 | /* When the packet rate is below pkt_rate_high but above 160 | * pkt_rate_low (both measured in packets per second) the 161 | * normal {rx,tx}_* coalescing parameters are used. 162 | */ 163 | 164 | /* When the packet rate is (measured in packets per second) 165 | * is above pkt_rate_high, the {rx,tx}_*_high parameters are 166 | * used. 167 | */ 168 | u32 pkt_rate_high; 169 | u32 rx_coalesce_usecs_high; 170 | u32 rx_max_coalesced_frames_high; 171 | u32 tx_coalesce_usecs_high; 172 | u32 tx_max_coalesced_frames_high; 173 | 174 | /* How often to do adaptive coalescing packet rate sampling, 175 | * measured in seconds. Must not be zero. 176 | */ 177 | u32 rate_sample_interval; 178 | }; 179 | 180 | /* for configuring RX/TX ring parameters */ 181 | struct ethtool_ringparam { 182 | u32 cmd; /* ETHTOOL_{G,S}RINGPARAM */ 183 | 184 | /* Read only attributes. These indicate the maximum number 185 | * of pending RX/TX ring entries the driver will allow the 186 | * user to set. 187 | */ 188 | u32 rx_max_pending; 189 | u32 rx_mini_max_pending; 190 | u32 rx_jumbo_max_pending; 191 | u32 tx_max_pending; 192 | 193 | /* Values changeable by the user. The valid values are 194 | * in the range 1 to the "*_max_pending" counterpart above. 195 | */ 196 | u32 rx_pending; 197 | u32 rx_mini_pending; 198 | u32 rx_jumbo_pending; 199 | u32 tx_pending; 200 | }; 201 | 202 | /* for configuring link flow control parameters */ 203 | struct ethtool_pauseparam { 204 | u32 cmd; /* ETHTOOL_{G,S}PAUSEPARAM */ 205 | 206 | /* If the link is being auto-negotiated (via ethtool_cmd.autoneg 207 | * being true) the user may set 'autonet' here non-zero to have the 208 | * pause parameters be auto-negotiated too. In such a case, the 209 | * {rx,tx}_pause values below determine what capabilities are 210 | * advertised. 211 | * 212 | * If 'autoneg' is zero or the link is not being auto-negotiated, 213 | * then {rx,tx}_pause force the driver to use/not-use pause 214 | * flow control. 215 | */ 216 | u32 autoneg; 217 | u32 rx_pause; 218 | u32 tx_pause; 219 | }; 220 | 221 | #define ETH_GSTRING_LEN 32 222 | enum ethtool_stringset { 223 | ETH_SS_TEST = 0, 224 | ETH_SS_STATS, 225 | }; 226 | 227 | /* for passing string sets for data tagging */ 228 | struct ethtool_gstrings { 229 | u32 cmd; /* ETHTOOL_GSTRINGS */ 230 | u32 string_set; /* string set id e.c. ETH_SS_TEST, etc*/ 231 | u32 len; /* number of strings in the string set */ 232 | u8 data[0]; 233 | }; 234 | 235 | enum ethtool_test_flags { 236 | ETH_TEST_FL_OFFLINE = (1 << 0), /* online / offline */ 237 | ETH_TEST_FL_FAILED = (1 << 1), /* test passed / failed */ 238 | }; 239 | 240 | /* for requesting NIC test and getting results*/ 241 | struct ethtool_test { 242 | u32 cmd; /* ETHTOOL_TEST */ 243 | u32 flags; /* ETH_TEST_FL_xxx */ 244 | u32 reserved; 245 | u32 len; /* result length, in number of u64 elements */ 246 | u64 data[0]; 247 | }; 248 | 249 | /* for dumping NIC-specific statistics */ 250 | struct ethtool_stats { 251 | u32 cmd; /* ETHTOOL_GSTATS */ 252 | u32 n_stats; /* number of u64's being returned */ 253 | u64 data[0]; 254 | }; 255 | 256 | /* CMDs currently supported */ 257 | #define ETHTOOL_GSET 0x00000001 /* Get settings. */ 258 | #define ETHTOOL_SSET 0x00000002 /* Set settings, privileged. */ 259 | #define ETHTOOL_GDRVINFO 0x00000003 /* Get driver info. */ 260 | #define ETHTOOL_GREGS 0x00000004 /* Get NIC registers, privileged. */ 261 | #define ETHTOOL_GWOL 0x00000005 /* Get wake-on-lan options. */ 262 | #define ETHTOOL_SWOL 0x00000006 /* Set wake-on-lan options, priv. */ 263 | #define ETHTOOL_GMSGLVL 0x00000007 /* Get driver message level */ 264 | #define ETHTOOL_SMSGLVL 0x00000008 /* Set driver msg level, priv. */ 265 | #define ETHTOOL_NWAY_RST 0x00000009 /* Restart autonegotiation, priv. */ 266 | #define ETHTOOL_GLINK 0x0000000a /* Get link status (ethtool_value) */ 267 | #define ETHTOOL_GEEPROM 0x0000000b /* Get EEPROM data */ 268 | #define ETHTOOL_SEEPROM 0x0000000c /* Set EEPROM data, priv. */ 269 | #define ETHTOOL_GCOALESCE 0x0000000e /* Get coalesce config */ 270 | #define ETHTOOL_SCOALESCE 0x0000000f /* Set coalesce config, priv. */ 271 | #define ETHTOOL_GRINGPARAM 0x00000010 /* Get ring parameters */ 272 | #define ETHTOOL_SRINGPARAM 0x00000011 /* Set ring parameters, priv. */ 273 | #define ETHTOOL_GPAUSEPARAM 0x00000012 /* Get pause parameters */ 274 | #define ETHTOOL_SPAUSEPARAM 0x00000013 /* Set pause parameters, priv. */ 275 | #define ETHTOOL_GRXCSUM 0x00000014 /* Get RX hw csum enable (e.v.) */ 276 | #define ETHTOOL_SRXCSUM 0x00000015 /* Set RX hw csum enable (e.v.) */ 277 | #define ETHTOOL_GTXCSUM 0x00000016 /* Get TX hw csum enable (e.v.) */ 278 | #define ETHTOOL_STXCSUM 0x00000017 /* Set TX hw csum enable (e.v.) */ 279 | #define ETHTOOL_GSG 0x00000018 /* Get scatter-gather enable 280 | * (ethtool_value) */ 281 | #define ETHTOOL_SSG 0x00000019 /* Set scatter-gather enable 282 | * (ethtool_value), priv. */ 283 | #define ETHTOOL_TEST 0x0000001a /* execute NIC self-test, priv. */ 284 | #define ETHTOOL_GSTRINGS 0x0000001b /* get specified string set */ 285 | #define ETHTOOL_PHYS_ID 0x0000001c /* identify the NIC */ 286 | #define ETHTOOL_GSTATS 0x0000001d /* get NIC-specific statistics */ 287 | #define ETHTOOL_GTSO 0x0000001e /* Get TSO enable (e.v.) */ 288 | #define ETHTOOL_STSO 0x0000001f /* Set TSO enable (e.v.) */ 289 | #define ETHTOOL_GUFO 0x00000021 /* Get UFO enable (e.v.) */ 290 | #define ETHTOOL_SUFO 0x00000022 /* Set UFO enable (e.v.) */ 291 | #define ETHTOOL_GGSO 0x00000023 /* Get GSO enable (e.v.) */ 292 | #define ETHTOOL_SGSO 0x00000024 /* Set GSO enable (e.v.) */ 293 | #define ETHTOOL_GGRO 0x0000002b /* Get GRO enable (e.v.) */ 294 | #define ETHTOOL_SGRO 0x0000002c /* Set GRO enable (e.v.) */ 295 | 296 | /* compatibility with older code */ 297 | #define SPARC_ETH_GSET ETHTOOL_GSET 298 | #define SPARC_ETH_SSET ETHTOOL_SSET 299 | 300 | /* Indicates what features are supported by the interface. */ 301 | #define SUPPORTED_10baseT_Half (1 << 0) 302 | #define SUPPORTED_10baseT_Full (1 << 1) 303 | #define SUPPORTED_100baseT_Half (1 << 2) 304 | #define SUPPORTED_100baseT_Full (1 << 3) 305 | #define SUPPORTED_1000baseT_Half (1 << 4) 306 | #define SUPPORTED_1000baseT_Full (1 << 5) 307 | #define SUPPORTED_Autoneg (1 << 6) 308 | #define SUPPORTED_TP (1 << 7) 309 | #define SUPPORTED_AUI (1 << 8) 310 | #define SUPPORTED_MII (1 << 9) 311 | #define SUPPORTED_FIBRE (1 << 10) 312 | #define SUPPORTED_BNC (1 << 11) 313 | #define SUPPORTED_10000baseT_Full (1 << 12) 314 | 315 | /* Indicates what features are advertised by the interface. */ 316 | #define ADVERTISED_10baseT_Half (1 << 0) 317 | #define ADVERTISED_10baseT_Full (1 << 1) 318 | #define ADVERTISED_100baseT_Half (1 << 2) 319 | #define ADVERTISED_100baseT_Full (1 << 3) 320 | #define ADVERTISED_1000baseT_Half (1 << 4) 321 | #define ADVERTISED_1000baseT_Full (1 << 5) 322 | #define ADVERTISED_Autoneg (1 << 6) 323 | #define ADVERTISED_TP (1 << 7) 324 | #define ADVERTISED_AUI (1 << 8) 325 | #define ADVERTISED_MII (1 << 9) 326 | #define ADVERTISED_FIBRE (1 << 10) 327 | #define ADVERTISED_BNC (1 << 11) 328 | #define ADVERTISED_10000baseT_Full (1 << 12) 329 | 330 | /* The following are all involved in forcing a particular link 331 | * mode for the device for setting things. When getting the 332 | * devices settings, these indicate the current mode and whether 333 | * it was foced up into this mode or autonegotiated. 334 | */ 335 | 336 | /* The forced speed, 10Mb, 100Mb, gigabit, 10GbE. */ 337 | #define SPEED_10 10 338 | #define SPEED_100 100 339 | #define SPEED_1000 1000 340 | #define SPEED_10000 10000 341 | 342 | /* Duplex, half or full. */ 343 | #define DUPLEX_HALF 0x00 344 | #define DUPLEX_FULL 0x01 345 | 346 | /* Which connector port. */ 347 | #define PORT_TP 0x00 348 | #define PORT_AUI 0x01 349 | #define PORT_MII 0x02 350 | #define PORT_FIBRE 0x03 351 | #define PORT_BNC 0x04 352 | 353 | /* Which tranceiver to use. */ 354 | #define XCVR_INTERNAL 0x00 355 | #define XCVR_EXTERNAL 0x01 356 | #define XCVR_DUMMY1 0x02 357 | #define XCVR_DUMMY2 0x03 358 | #define XCVR_DUMMY3 0x04 359 | 360 | /* Enable or disable autonegotiation. If this is set to enable, 361 | * the forced link modes above are completely ignored. 362 | */ 363 | #define AUTONEG_DISABLE 0x00 364 | #define AUTONEG_ENABLE 0x01 365 | 366 | /* Wake-On-Lan options. */ 367 | #define WAKE_PHY (1 << 0) 368 | #define WAKE_UCAST (1 << 1) 369 | #define WAKE_MCAST (1 << 2) 370 | #define WAKE_BCAST (1 << 3) 371 | #define WAKE_ARP (1 << 4) 372 | #define WAKE_MAGIC (1 << 5) 373 | #define WAKE_MAGICSECURE (1 << 6) /* only meaningful if WAKE_MAGIC */ 374 | 375 | #endif /* _LINUX_ETHTOOL_H */ 376 | -------------------------------------------------------------------------------- /python-ethtool/ethtool.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2008-2013 Red Hat Inc. 3 | * 4 | * Arnaldo Carvalho de Melo 5 | * David Sommerseth 6 | * 7 | * First bits from a Red Hat config tool by Harald Hoyer. 8 | * 9 | * This application is free software; you can redistribute it and/or modify it 10 | * under the terms of the GNU General Public License as published by the Free 11 | * Software Foundation; version 2. 12 | * 13 | * This application is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * General Public License for more details. 17 | */ 18 | #include 19 | #include "include/py3c/compat.h" 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #if !defined IFF_UP 34 | #include 35 | #endif 36 | 37 | #include "etherinfo_struct.h" 38 | #include "etherinfo_obj.h" 39 | #include "etherinfo.h" 40 | 41 | extern PyTypeObject PyEtherInfo_Type; 42 | 43 | #ifndef IFF_DYNAMIC 44 | #define IFF_DYNAMIC 0x8000 /* dialup device with changing addresses*/ 45 | #endif 46 | 47 | typedef unsigned long long u64; 48 | typedef __uint32_t u32; 49 | typedef __uint16_t u16; 50 | typedef __uint8_t u8; 51 | 52 | #include "ethtool-copy.h" 53 | #include /* for SIOCETHTOOL */ 54 | 55 | #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) 56 | 57 | #define _PATH_PROCNET_DEV "/proc/net/dev" 58 | 59 | static PyObject *get_active_devices(PyObject *self __unused, 60 | PyObject *args __unused) 61 | { 62 | PyObject *list; 63 | struct ifaddrs *ifaddr, *ifa; 64 | 65 | if (getifaddrs(&ifaddr) == -1) 66 | return PyErr_SetFromErrno(PyExc_OSError); 67 | 68 | list = PyList_New(0); 69 | for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { 70 | PyObject *str = PyStr_FromString(ifa->ifa_name); 71 | /* names are not unique (listed for both ipv4 and ipv6) */ 72 | if (!PySequence_Contains(list, str) && (ifa->ifa_flags & (IFF_UP))) { 73 | PyList_Append(list, str); 74 | } 75 | Py_DECREF(str); 76 | } 77 | 78 | freeifaddrs(ifaddr); 79 | 80 | return list; 81 | } 82 | 83 | static PyObject *get_devices(PyObject *self __unused, PyObject *args __unused) 84 | { 85 | char buffer[256]; 86 | char *ret; 87 | PyObject *list = PyList_New(0); 88 | FILE *fd = fopen(_PATH_PROCNET_DEV, "r"); 89 | 90 | if (fd == NULL) { 91 | return PyErr_SetFromErrno(PyExc_OSError); 92 | } 93 | /* skip over first two lines */ 94 | ret = fgets(buffer, 256, fd); 95 | ret = fgets(buffer, 256, fd); 96 | if (!ret) { 97 | fclose(fd); 98 | return PyErr_SetFromErrno(PyExc_OSError); 99 | } 100 | 101 | while (!feof(fd)) { 102 | PyObject *str; 103 | char *name = buffer; 104 | char *end = buffer; 105 | 106 | if (fgets(buffer, 256, fd) == NULL) 107 | break; 108 | /* find colon */ 109 | while (*end && *end != ':') 110 | end++; 111 | *end = 0; /* terminate where colon was */ 112 | while (*name == ' ') 113 | name++; /* skip over leading whitespace if any */ 114 | 115 | str = PyStr_FromString(name); 116 | PyList_Append(list, str); 117 | Py_DECREF(str); 118 | } 119 | fclose(fd); 120 | return list; 121 | } 122 | 123 | static PyObject *get_hwaddress(PyObject *self __unused, PyObject *args) 124 | { 125 | struct ifreq ifr; 126 | int fd, err; 127 | const char *devname; 128 | char hwaddr[20]; 129 | 130 | if (!PyArg_ParseTuple(args, "s", &devname)) 131 | return NULL; 132 | 133 | /* Setup our request structure. */ 134 | memset(&ifr, 0, sizeof(ifr)); 135 | strncpy(&ifr.ifr_name[0], devname, IFNAMSIZ); 136 | ifr.ifr_name[IFNAMSIZ - 1] = 0; 137 | 138 | /* Open control socket. */ 139 | fd = socket(AF_INET, SOCK_DGRAM, 0); 140 | if (fd < 0) { 141 | return PyErr_SetFromErrno(PyExc_OSError); 142 | } 143 | 144 | /* Get current settings. */ 145 | err = ioctl(fd, SIOCGIFHWADDR, &ifr); 146 | if (err < 0) { 147 | PyErr_SetFromErrno(PyExc_IOError); 148 | close(fd); 149 | return NULL; 150 | } 151 | 152 | close(fd); 153 | 154 | sprintf(hwaddr, "%02x:%02x:%02x:%02x:%02x:%02x", 155 | (unsigned int)ifr.ifr_hwaddr.sa_data[0] % 256, 156 | (unsigned int)ifr.ifr_hwaddr.sa_data[1] % 256, 157 | (unsigned int)ifr.ifr_hwaddr.sa_data[2] % 256, 158 | (unsigned int)ifr.ifr_hwaddr.sa_data[3] % 256, 159 | (unsigned int)ifr.ifr_hwaddr.sa_data[4] % 256, 160 | (unsigned int)ifr.ifr_hwaddr.sa_data[5] % 256); 161 | 162 | return PyStr_FromString(hwaddr); 163 | } 164 | 165 | static PyObject *get_ipaddress(PyObject *self __unused, PyObject *args) 166 | { 167 | struct ifreq ifr; 168 | int fd, err; 169 | const char *devname; 170 | char ipaddr[20]; 171 | 172 | if (!PyArg_ParseTuple(args, "s", &devname)) 173 | return NULL; 174 | 175 | /* Setup our request structure. */ 176 | memset(&ifr, 0, sizeof(ifr)); 177 | strncpy(&ifr.ifr_name[0], devname, IFNAMSIZ); 178 | ifr.ifr_name[IFNAMSIZ - 1] = 0; 179 | 180 | /* Open control socket. */ 181 | fd = socket(AF_INET, SOCK_DGRAM, 0); 182 | if (fd < 0) { 183 | return PyErr_SetFromErrno(PyExc_OSError); 184 | } 185 | 186 | /* Get current settings. */ 187 | err = ioctl(fd, SIOCGIFADDR, &ifr); 188 | if (err < 0) { 189 | PyErr_SetFromErrno(PyExc_IOError); 190 | close(fd); 191 | return NULL; 192 | } 193 | 194 | close(fd); 195 | 196 | sprintf(ipaddr, "%u.%u.%u.%u", 197 | (unsigned int)ifr.ifr_addr.sa_data[2] % 256, 198 | (unsigned int)ifr.ifr_addr.sa_data[3] % 256, 199 | (unsigned int)ifr.ifr_addr.sa_data[4] % 256, 200 | (unsigned int)ifr.ifr_addr.sa_data[5] % 256); 201 | 202 | return PyStr_FromString(ipaddr); 203 | } 204 | 205 | 206 | /** 207 | * Retrieves the current information about all interfaces. 208 | * All interfaces will be returned as a list of objects per interface. 209 | * 210 | * @param self Not used 211 | * @param args Python arguments - device name(s) as either a string or a list 212 | * 213 | * @return Python list of objects on success, otherwise NULL. 214 | */ 215 | static PyObject *get_interfaces_info(PyObject *self __unused, PyObject *args) { 216 | PyObject *devlist = NULL; 217 | PyObject *inargs = NULL; 218 | char **fetch_devs = NULL; 219 | int i = 0, fetch_devs_len = 0; 220 | 221 | if (!PyArg_ParseTuple(args, "|O", &inargs)) { 222 | PyErr_SetString(PyExc_LookupError, 223 | "Argument must be either a string, list or a tuple"); 224 | return NULL; 225 | } 226 | 227 | /* Parse input arguments if we got them */ 228 | if (inargs != NULL) { 229 | if (PyStr_Check(inargs)) { /* Input argument is just a string */ 230 | fetch_devs_len = 1; 231 | fetch_devs = calloc(1, sizeof(char *)); 232 | fetch_devs[0] = PyStr_AsString(inargs); 233 | 234 | } else if (PyTuple_Check(inargs)) { 235 | /* Input argument is a tuple list with devices */ 236 | int j = 0; 237 | 238 | fetch_devs_len = PyTuple_Size(inargs); 239 | fetch_devs = calloc(fetch_devs_len+1, sizeof(char *)); 240 | for (i = 0; i < fetch_devs_len; i++) { 241 | PyObject *elmt = PyTuple_GetItem(inargs, i); 242 | if (elmt && PyStr_Check(elmt)) { 243 | fetch_devs[j++] = PyStr_AsString(elmt); 244 | } 245 | } 246 | fetch_devs_len = j; 247 | } else if (PyList_Check(inargs)) { 248 | /* Input argument is a list with devices */ 249 | int j = 0; 250 | 251 | fetch_devs_len = PyList_Size(inargs); 252 | fetch_devs = calloc(fetch_devs_len+1, sizeof(char *)); 253 | for (i = 0; i < fetch_devs_len; i++) { 254 | PyObject *elmt = PyList_GetItem(inargs, i); 255 | if (elmt && PyStr_Check(elmt)) { 256 | fetch_devs[j++] = PyStr_AsString(elmt); 257 | } 258 | } 259 | fetch_devs_len = j; 260 | } else { 261 | PyErr_SetString(PyExc_LookupError, 262 | "Argument must be either a string, " 263 | "list or a tuple"); 264 | return NULL; 265 | } 266 | } 267 | 268 | devlist = PyList_New(0); 269 | for (i = 0; i < fetch_devs_len; i++) { 270 | PyEtherInfo *dev = NULL; 271 | 272 | /* Store the device name and a reference to the NETLINK connection for 273 | * objects to use when quering for device info 274 | */ 275 | 276 | dev = PyObject_New(PyEtherInfo, &PyEtherInfo_Type); 277 | if (!dev) { 278 | PyErr_SetFromErrno(PyExc_OSError); 279 | free(fetch_devs); 280 | return NULL; 281 | } 282 | 283 | dev->device = PyStr_FromString(fetch_devs[i]); 284 | dev->hwaddress = NULL; 285 | dev->index = -1; 286 | 287 | /* Append device object to the device list */ 288 | PyList_Append(devlist, (PyObject *)dev); 289 | Py_DECREF(dev); 290 | } 291 | free(fetch_devs); 292 | 293 | return devlist; 294 | } 295 | 296 | 297 | static PyObject *get_flags (PyObject *self __unused, PyObject *args) 298 | { 299 | struct ifreq ifr; 300 | const char *devname; 301 | int fd, err; 302 | 303 | if (!PyArg_ParseTuple(args, "s", &devname)) 304 | return NULL; 305 | 306 | /* Setup our request structure. */ 307 | memset(&ifr, 0, sizeof(ifr)); 308 | strncpy(&ifr.ifr_name[0], devname, IFNAMSIZ); 309 | ifr.ifr_name[IFNAMSIZ - 1] = 0; 310 | 311 | /* Open control socket. */ 312 | fd = socket(AF_INET, SOCK_DGRAM, 0); 313 | if (fd < 0) { 314 | return PyErr_SetFromErrno(PyExc_OSError); 315 | } 316 | err = ioctl(fd, SIOCGIFFLAGS, &ifr); 317 | if(err < 0) { 318 | PyErr_SetFromErrno(PyExc_IOError); 319 | close(fd); 320 | return NULL; 321 | } 322 | 323 | close(fd); 324 | 325 | return Py_BuildValue("h", ifr.ifr_flags); 326 | } 327 | 328 | static PyObject *get_netmask (PyObject *self __unused, PyObject *args) 329 | { 330 | struct ifreq ifr; 331 | int fd, err; 332 | const char *devname; 333 | char netmask[20]; 334 | 335 | if (!PyArg_ParseTuple(args, "s", &devname)) 336 | return NULL; 337 | 338 | /* Setup our request structure. */ 339 | memset(&ifr, 0, sizeof(ifr)); 340 | strncpy(&ifr.ifr_name[0], devname, IFNAMSIZ); 341 | ifr.ifr_name[IFNAMSIZ - 1] = 0; 342 | 343 | /* Open control socket. */ 344 | fd = socket(AF_INET, SOCK_DGRAM, 0); 345 | if (fd < 0) { 346 | return PyErr_SetFromErrno(PyExc_OSError); 347 | } 348 | 349 | /* Get current settings. */ 350 | err = ioctl(fd, SIOCGIFNETMASK, &ifr); 351 | if (err < 0) { 352 | PyErr_SetFromErrno(PyExc_IOError); 353 | close(fd); 354 | return NULL; 355 | } 356 | 357 | close(fd); 358 | 359 | sprintf(netmask, "%u.%u.%u.%u", 360 | (unsigned int)ifr.ifr_netmask.sa_data[2] % 256, 361 | (unsigned int)ifr.ifr_netmask.sa_data[3] % 256, 362 | (unsigned int)ifr.ifr_netmask.sa_data[4] % 256, 363 | (unsigned int)ifr.ifr_netmask.sa_data[5] % 256); 364 | 365 | return PyStr_FromString(netmask); 366 | } 367 | 368 | static PyObject *get_broadcast(PyObject *self __unused, PyObject *args) 369 | { 370 | struct ifreq ifr; 371 | int fd, err; 372 | const char *devname; 373 | char broadcast[20]; 374 | 375 | if (!PyArg_ParseTuple(args, "s", &devname)) 376 | return NULL; 377 | 378 | /* Setup our request structure. */ 379 | memset(&ifr, 0, sizeof(ifr)); 380 | strncpy(&ifr.ifr_name[0], devname, IFNAMSIZ); 381 | ifr.ifr_name[IFNAMSIZ - 1] = 0; 382 | 383 | /* Open control socket. */ 384 | fd = socket(AF_INET, SOCK_DGRAM, 0); 385 | if (fd < 0) { 386 | return PyErr_SetFromErrno(PyExc_OSError); 387 | } 388 | 389 | /* Get current settings. */ 390 | err = ioctl(fd, SIOCGIFBRDADDR, &ifr); 391 | if (err < 0) { 392 | PyErr_SetFromErrno(PyExc_IOError); 393 | close(fd); 394 | return NULL; 395 | } 396 | 397 | close(fd); 398 | 399 | sprintf(broadcast, "%u.%u.%u.%u", 400 | (unsigned int)ifr.ifr_broadaddr.sa_data[2] % 256, 401 | (unsigned int)ifr.ifr_broadaddr.sa_data[3] % 256, 402 | (unsigned int)ifr.ifr_broadaddr.sa_data[4] % 256, 403 | (unsigned int)ifr.ifr_broadaddr.sa_data[5] % 256); 404 | 405 | return PyStr_FromString(broadcast); 406 | } 407 | 408 | static PyObject *get_module(PyObject *self __unused, PyObject *args) 409 | { 410 | struct ethtool_cmd ecmd; 411 | struct ifreq ifr; 412 | int fd, err; 413 | char buf[2048]; 414 | const char *devname; 415 | 416 | if (!PyArg_ParseTuple(args, "s", &devname)) 417 | return NULL; 418 | 419 | /* Setup our control structures. */ 420 | memset(&ecmd, 0, sizeof(ecmd)); 421 | memset(&ifr, 0, sizeof(ifr)); 422 | strncpy(&ifr.ifr_name[0], devname, IFNAMSIZ); 423 | ifr.ifr_name[IFNAMSIZ - 1] = 0; 424 | ifr.ifr_data = (caddr_t) &buf; 425 | ecmd.cmd = ETHTOOL_GDRVINFO; 426 | memcpy(&buf, &ecmd, sizeof(ecmd)); 427 | 428 | /* Open control socket. */ 429 | fd = socket(AF_INET, SOCK_DGRAM, 0); 430 | if (fd < 0) { 431 | return PyErr_SetFromErrno(PyExc_OSError); 432 | } 433 | 434 | /* Get current settings. */ 435 | err = ioctl(fd, SIOCETHTOOL, &ifr); 436 | 437 | if (err < 0) { /* failed? */ 438 | PyErr_SetFromErrno(PyExc_IOError); 439 | FILE *file; 440 | int found = 0; 441 | char driver[101], dev[101]; 442 | close(fd); 443 | 444 | /* Before bailing, maybe it is a PCMCIA/PC Card? */ 445 | file = fopen("/var/lib/pcmcia/stab", "r"); 446 | if (file == NULL) { 447 | return NULL; 448 | } 449 | 450 | while (!feof(file)) { 451 | if (fgets(buf, 2048, file) == NULL) 452 | break; 453 | buf[2047] = '\0'; 454 | if (strncmp(buf, "Socket", 6) != 0) { 455 | if (sscanf(buf, "%*d\t%*s\t%100s\t%*d\t%100s\n", 456 | driver, dev) > 0) { 457 | driver[99] = '\0'; 458 | dev[99] = '\0'; 459 | if (strcmp(devname, dev) == 0) { 460 | found = 1; 461 | break; 462 | } 463 | } 464 | } 465 | } 466 | fclose(file); 467 | if (!found) { 468 | return NULL; 469 | } else { 470 | PyErr_Clear(); 471 | return PyStr_FromString(driver); 472 | } 473 | } 474 | 475 | close(fd); 476 | return PyStr_FromString(((struct ethtool_drvinfo *)buf)->driver); 477 | } 478 | 479 | static PyObject *get_businfo(PyObject *self __unused, PyObject *args) 480 | { 481 | struct ethtool_cmd ecmd; 482 | struct ifreq ifr; 483 | int fd, err; 484 | char buf[1024]; 485 | const char *devname; 486 | 487 | if (!PyArg_ParseTuple(args, "s", &devname)) 488 | return NULL; 489 | 490 | /* Setup our control structures. */ 491 | memset(&ecmd, 0, sizeof(ecmd)); 492 | memset(&ifr, 0, sizeof(ifr)); 493 | strncpy(&ifr.ifr_name[0], devname, IFNAMSIZ); 494 | ifr.ifr_name[IFNAMSIZ - 1] = 0; 495 | ifr.ifr_data = (caddr_t) &buf; 496 | ecmd.cmd = ETHTOOL_GDRVINFO; 497 | memcpy(&buf, &ecmd, sizeof(ecmd)); 498 | 499 | /* Open control socket. */ 500 | fd = socket(AF_INET, SOCK_DGRAM, 0); 501 | if (fd < 0) { 502 | return PyErr_SetFromErrno(PyExc_OSError); 503 | } 504 | 505 | /* Get current settings. */ 506 | err = ioctl(fd, SIOCETHTOOL, &ifr); 507 | 508 | if (err < 0) { /* failed? */ 509 | PyErr_SetFromErrno(PyExc_IOError); 510 | close(fd); 511 | return NULL; 512 | } 513 | 514 | close(fd); 515 | return PyStr_FromString(((struct ethtool_drvinfo *)buf)->bus_info); 516 | } 517 | 518 | static int send_command(int cmd, const char *devname, void *value) 519 | { 520 | /* Setup our request structure. */ 521 | int fd, err; 522 | struct ifreq ifr; 523 | struct ethtool_value *eval = value; 524 | 525 | memset(&ifr, 0, sizeof(ifr)); 526 | strncpy(&ifr.ifr_name[0], devname, IFNAMSIZ); 527 | ifr.ifr_name[IFNAMSIZ - 1] = 0; 528 | ifr.ifr_data = (caddr_t)eval; 529 | eval->cmd = cmd; 530 | 531 | /* Open control socket. */ 532 | fd = socket(AF_INET, SOCK_DGRAM, 0); 533 | if (fd < 0) { 534 | PyErr_SetFromErrno(PyExc_OSError); 535 | return -1; 536 | } 537 | 538 | /* Get current settings. */ 539 | err = ioctl(fd, SIOCETHTOOL, &ifr); 540 | if (err < 0) { 541 | PyErr_SetFromErrno(PyExc_IOError); 542 | } 543 | 544 | close(fd); 545 | return err; 546 | } 547 | 548 | static int get_dev_value(int cmd, PyObject *args, void *value) 549 | { 550 | const char *devname; 551 | int err = -1; 552 | 553 | if (PyArg_ParseTuple(args, "s", &devname)) 554 | err = send_command(cmd, devname, value); 555 | 556 | return err; 557 | } 558 | 559 | static int get_dev_int_value(int cmd, PyObject *args, int *value) 560 | { 561 | struct ethtool_value eval; 562 | int rc = get_dev_value(cmd, args, &eval); 563 | 564 | if (rc == 0) 565 | *value = *(int *)&eval.data; 566 | 567 | return rc; 568 | } 569 | 570 | static int dev_set_int_value(int cmd, PyObject *args) 571 | { 572 | struct ethtool_value eval; 573 | const char *devname; 574 | 575 | if (!PyArg_ParseTuple(args, "si", &devname, &eval.data)) 576 | return -1; 577 | 578 | return send_command(cmd, devname, &eval); 579 | } 580 | 581 | static PyObject *get_tso(PyObject *self __unused, PyObject *args) 582 | { 583 | int value = 0; 584 | 585 | if (get_dev_int_value(ETHTOOL_GTSO, args, &value) < 0) 586 | return NULL; 587 | 588 | return Py_BuildValue("b", value); 589 | } 590 | 591 | static PyObject *set_tso(PyObject *self __unused, PyObject *args) 592 | { 593 | if (dev_set_int_value(ETHTOOL_STSO, args) < 0) 594 | return NULL; 595 | 596 | Py_RETURN_NONE; 597 | } 598 | 599 | static PyObject *get_ufo(PyObject *self __unused, PyObject *args) 600 | { 601 | int value = 0; 602 | 603 | if (get_dev_int_value(ETHTOOL_GUFO, args, &value) < 0) 604 | return NULL; 605 | 606 | return Py_BuildValue("b", value); 607 | } 608 | 609 | static PyObject *get_gso(PyObject *self __unused, PyObject *args) 610 | { 611 | int value = 0; 612 | 613 | if (get_dev_int_value(ETHTOOL_GGSO, args, &value) < 0) 614 | return NULL; 615 | 616 | return Py_BuildValue("b", value); 617 | } 618 | 619 | static PyObject *set_gso(PyObject *self __unused, PyObject *args) 620 | { 621 | if (dev_set_int_value(ETHTOOL_SGSO, args) < 0) 622 | return NULL; 623 | 624 | Py_RETURN_NONE; 625 | } 626 | 627 | static PyObject *get_gro(PyObject *self __unused, PyObject *args) 628 | { 629 | int value = 0; 630 | 631 | if (get_dev_int_value(ETHTOOL_GGRO, args, &value) < 0) 632 | return NULL; 633 | 634 | return Py_BuildValue("b", value); 635 | } 636 | 637 | static PyObject *set_gro(PyObject *self __unused, PyObject *args) 638 | { 639 | if (dev_set_int_value(ETHTOOL_SGRO, args) < 0) 640 | return NULL; 641 | 642 | Py_RETURN_NONE; 643 | } 644 | 645 | static PyObject *get_sg(PyObject *self __unused, PyObject *args) 646 | { 647 | int value = 0; 648 | 649 | if (get_dev_int_value(ETHTOOL_GSG, args, &value) < 0) 650 | return NULL; 651 | 652 | return Py_BuildValue("b", value); 653 | } 654 | 655 | static PyObject *get_wireless_protocol (PyObject *self __unused, PyObject *args) 656 | { 657 | struct iwreq iwr; 658 | const char *devname; 659 | int fd, err; 660 | 661 | if (!PyArg_ParseTuple(args, "s", &devname)) 662 | return NULL; 663 | 664 | /* Setup our request structure. */ 665 | memset(&iwr, 0, sizeof(iwr)); 666 | strncpy(iwr.ifr_name, devname, IFNAMSIZ-1); 667 | iwr.ifr_name[IFNAMSIZ-1] = 0; 668 | 669 | /* Open control socket. */ 670 | fd = socket(AF_INET, SOCK_DGRAM, 0); 671 | if (fd < 0) { 672 | PyErr_SetFromErrno(PyExc_OSError); 673 | return NULL; 674 | } 675 | err = ioctl(fd, SIOCGIWNAME, &iwr); 676 | if(err < 0) { 677 | PyErr_SetFromErrno(PyExc_IOError); 678 | close(fd); 679 | return NULL; 680 | } 681 | 682 | close(fd); 683 | 684 | return PyStr_FromString(iwr.u.name); 685 | } 686 | 687 | struct struct_desc { 688 | char *name; 689 | unsigned short offset; 690 | unsigned short size; 691 | }; 692 | 693 | #define member_desc(type, member_name) { \ 694 | .name = #member_name, \ 695 | .offset = offsetof(type, member_name), \ 696 | .size = sizeof(((type *)0)->member_name), } 697 | 698 | struct struct_desc ethtool_coalesce_desc[] = { 699 | member_desc(struct ethtool_coalesce, rx_coalesce_usecs), 700 | member_desc(struct ethtool_coalesce, rx_max_coalesced_frames), 701 | member_desc(struct ethtool_coalesce, rx_coalesce_usecs_irq), 702 | member_desc(struct ethtool_coalesce, rx_max_coalesced_frames_irq), 703 | member_desc(struct ethtool_coalesce, tx_coalesce_usecs), 704 | member_desc(struct ethtool_coalesce, tx_max_coalesced_frames), 705 | member_desc(struct ethtool_coalesce, tx_coalesce_usecs_irq), 706 | member_desc(struct ethtool_coalesce, tx_max_coalesced_frames_irq), 707 | member_desc(struct ethtool_coalesce, stats_block_coalesce_usecs), 708 | member_desc(struct ethtool_coalesce, use_adaptive_rx_coalesce), 709 | member_desc(struct ethtool_coalesce, use_adaptive_tx_coalesce), 710 | member_desc(struct ethtool_coalesce, pkt_rate_low), 711 | member_desc(struct ethtool_coalesce, rx_coalesce_usecs_low), 712 | member_desc(struct ethtool_coalesce, rx_max_coalesced_frames_low), 713 | member_desc(struct ethtool_coalesce, tx_coalesce_usecs_low), 714 | member_desc(struct ethtool_coalesce, tx_max_coalesced_frames_low), 715 | member_desc(struct ethtool_coalesce, pkt_rate_high), 716 | member_desc(struct ethtool_coalesce, rx_coalesce_usecs_high), 717 | member_desc(struct ethtool_coalesce, rx_max_coalesced_frames_high), 718 | member_desc(struct ethtool_coalesce, tx_coalesce_usecs_high), 719 | member_desc(struct ethtool_coalesce, tx_max_coalesced_frames_high), 720 | member_desc(struct ethtool_coalesce, rate_sample_interval), 721 | }; 722 | 723 | static PyObject *__struct_desc_create_dict(struct struct_desc *table, 724 | int nr_entries, void *values) 725 | { 726 | int i; 727 | PyObject *dict = PyDict_New(); 728 | 729 | if (dict == NULL) 730 | return NULL; 731 | 732 | for (i = 0; i < nr_entries; ++i) { 733 | struct struct_desc *d = &table[i]; 734 | PyObject *objval = NULL; 735 | void *val = values + d->offset; 736 | 737 | switch (d->size) { 738 | case sizeof(uint32_t): 739 | objval = PyLong_FromLong(*(uint32_t *)val); 740 | break; 741 | } 742 | 743 | if (objval == NULL) { 744 | Py_DECREF(dict); 745 | return NULL; 746 | } 747 | 748 | if (PyDict_SetItemString(dict, d->name, objval) != 0) { 749 | Py_DECREF(objval); 750 | Py_DECREF(dict); 751 | return NULL; 752 | } 753 | 754 | Py_DECREF(objval); 755 | } 756 | 757 | return dict; 758 | } 759 | 760 | #define struct_desc_create_dict(table, values) \ 761 | __struct_desc_create_dict(table, ARRAY_SIZE(table), values) 762 | 763 | static int __struct_desc_from_dict(struct struct_desc *table, 764 | int nr_entries, void *to, PyObject *dict) 765 | { 766 | char buf[2048]; 767 | int i; 768 | 769 | for (i = 0; i < nr_entries; ++i) { 770 | struct struct_desc *d = &table[i]; 771 | void *val = to + d->offset; 772 | PyObject *obj; 773 | 774 | switch (d->size) { 775 | case sizeof(uint32_t): 776 | obj = PyDict_GetItemString(dict, d->name); 777 | if (obj == NULL) { 778 | snprintf(buf, sizeof(buf), 779 | "Missing dict entry for field %s", 780 | d->name); 781 | PyErr_SetString(PyExc_IOError, buf); 782 | return -1; 783 | } 784 | *(uint32_t *)val = PyLong_AsLong(obj); 785 | break; 786 | default: 787 | snprintf(buf, sizeof(buf), 788 | "Invalid type size %d for field %s", 789 | d->size, d->name); 790 | PyErr_SetString(PyExc_IOError, buf); 791 | return -1; 792 | } 793 | } 794 | 795 | return 0; 796 | } 797 | 798 | #define struct_desc_from_dict(table, to, dict) \ 799 | __struct_desc_from_dict(table, ARRAY_SIZE(table), to, dict) 800 | 801 | static PyObject *get_coalesce(PyObject *self __unused, PyObject *args) 802 | { 803 | struct ethtool_coalesce coal; 804 | 805 | if (get_dev_value(ETHTOOL_GCOALESCE, args, &coal) < 0) 806 | return NULL; 807 | 808 | return struct_desc_create_dict(ethtool_coalesce_desc, &coal); 809 | } 810 | 811 | static PyObject *set_coalesce(PyObject *self __unused, PyObject *args) 812 | { 813 | struct ethtool_coalesce coal; 814 | const char *devname; 815 | PyObject *dict; 816 | 817 | if (!PyArg_ParseTuple(args, "sO", &devname, &dict)) 818 | return NULL; 819 | 820 | if (struct_desc_from_dict(ethtool_coalesce_desc, &coal, dict) != 0) 821 | return NULL; 822 | 823 | if (send_command(ETHTOOL_SCOALESCE, devname, &coal)) 824 | return NULL; 825 | 826 | Py_RETURN_NONE; 827 | } 828 | 829 | struct struct_desc ethtool_ringparam_desc[] = { 830 | member_desc(struct ethtool_ringparam, rx_max_pending), 831 | member_desc(struct ethtool_ringparam, rx_mini_max_pending), 832 | member_desc(struct ethtool_ringparam, rx_jumbo_max_pending), 833 | member_desc(struct ethtool_ringparam, tx_max_pending), 834 | member_desc(struct ethtool_ringparam, rx_pending), 835 | member_desc(struct ethtool_ringparam, rx_mini_pending), 836 | member_desc(struct ethtool_ringparam, rx_jumbo_pending), 837 | member_desc(struct ethtool_ringparam, tx_pending), 838 | }; 839 | 840 | static PyObject *get_ringparam(PyObject *self __unused, PyObject *args) 841 | { 842 | struct ethtool_ringparam ring; 843 | 844 | if (get_dev_value(ETHTOOL_GRINGPARAM, args, &ring) < 0) 845 | return NULL; 846 | 847 | return struct_desc_create_dict(ethtool_ringparam_desc, &ring); 848 | } 849 | 850 | static PyObject *set_ringparam(PyObject *self __unused, PyObject *args) 851 | { 852 | struct ethtool_ringparam ring; 853 | const char *devname; 854 | PyObject *dict; 855 | 856 | if (!PyArg_ParseTuple(args, "sO", &devname, &dict)) 857 | return NULL; 858 | 859 | if (struct_desc_from_dict(ethtool_ringparam_desc, &ring, dict) != 0) 860 | return NULL; 861 | 862 | if (send_command(ETHTOOL_SRINGPARAM, devname, &ring)) 863 | return NULL; 864 | 865 | Py_RETURN_NONE; 866 | } 867 | 868 | static struct PyMethodDef PyEthModuleMethods[] = { 869 | { 870 | .ml_name = "get_module", 871 | .ml_meth = (PyCFunction)get_module, 872 | .ml_flags = METH_VARARGS, 873 | }, 874 | { 875 | .ml_name = "get_businfo", 876 | .ml_meth = (PyCFunction)get_businfo, 877 | .ml_flags = METH_VARARGS, 878 | }, 879 | { 880 | .ml_name = "get_hwaddr", 881 | .ml_meth = (PyCFunction)get_hwaddress, 882 | .ml_flags = METH_VARARGS, 883 | }, 884 | { 885 | .ml_name = "get_ipaddr", 886 | .ml_meth = (PyCFunction)get_ipaddress, 887 | .ml_flags = METH_VARARGS, 888 | }, 889 | { 890 | .ml_name = "get_interfaces_info", 891 | .ml_meth = (PyCFunction)get_interfaces_info, 892 | .ml_flags = METH_VARARGS, 893 | .ml_doc = "Accepts a string, list or tupples of interface names. " 894 | "Returns a list of ethtool.etherinfo objets with device information." 895 | }, 896 | { 897 | .ml_name = "get_netmask", 898 | .ml_meth = (PyCFunction)get_netmask, 899 | .ml_flags = METH_VARARGS, 900 | }, 901 | { 902 | .ml_name = "get_broadcast", 903 | .ml_meth = (PyCFunction)get_broadcast, 904 | .ml_flags = METH_VARARGS, 905 | }, 906 | { 907 | .ml_name = "get_coalesce", 908 | .ml_meth = (PyCFunction)get_coalesce, 909 | .ml_flags = METH_VARARGS, 910 | }, 911 | { 912 | .ml_name = "set_coalesce", 913 | .ml_meth = (PyCFunction)set_coalesce, 914 | .ml_flags = METH_VARARGS, 915 | }, 916 | { 917 | .ml_name = "get_devices", 918 | .ml_meth = (PyCFunction)get_devices, 919 | .ml_flags = METH_VARARGS, 920 | }, 921 | { 922 | .ml_name = "get_active_devices", 923 | .ml_meth = (PyCFunction)get_active_devices, 924 | .ml_flags = METH_VARARGS, 925 | }, 926 | { 927 | .ml_name = "get_ringparam", 928 | .ml_meth = (PyCFunction)get_ringparam, 929 | .ml_flags = METH_VARARGS, 930 | }, 931 | { 932 | .ml_name = "set_ringparam", 933 | .ml_meth = (PyCFunction)set_ringparam, 934 | .ml_flags = METH_VARARGS, 935 | }, 936 | { 937 | .ml_name = "get_tso", 938 | .ml_meth = (PyCFunction)get_tso, 939 | .ml_flags = METH_VARARGS, 940 | }, 941 | { 942 | .ml_name = "set_tso", 943 | .ml_meth = (PyCFunction)set_tso, 944 | .ml_flags = METH_VARARGS, 945 | }, 946 | { 947 | .ml_name = "get_ufo", 948 | .ml_meth = (PyCFunction)get_ufo, 949 | .ml_flags = METH_VARARGS, 950 | }, 951 | { 952 | .ml_name = "get_gso", 953 | .ml_meth = (PyCFunction)get_gso, 954 | .ml_flags = METH_VARARGS, 955 | }, 956 | { 957 | .ml_name = "set_gso", 958 | .ml_meth = (PyCFunction)set_gso, 959 | .ml_flags = METH_VARARGS, 960 | }, 961 | { 962 | .ml_name = "get_gro", 963 | .ml_meth = (PyCFunction)get_gro, 964 | .ml_flags = METH_VARARGS, 965 | }, 966 | { 967 | .ml_name = "set_gro", 968 | .ml_meth = (PyCFunction)set_gro, 969 | .ml_flags = METH_VARARGS, 970 | }, 971 | { 972 | .ml_name = "get_sg", 973 | .ml_meth = (PyCFunction)get_sg, 974 | .ml_flags = METH_VARARGS, 975 | }, 976 | { 977 | .ml_name = "get_flags", 978 | .ml_meth = (PyCFunction)get_flags, 979 | .ml_flags = METH_VARARGS, 980 | }, 981 | { 982 | .ml_name = "get_wireless_protocol", 983 | .ml_meth = (PyCFunction)get_wireless_protocol, 984 | .ml_flags = METH_VARARGS, 985 | }, 986 | { .ml_name = NULL, }, 987 | }; 988 | 989 | static struct PyModuleDef moduledef = { 990 | PyModuleDef_HEAD_INIT, 991 | .m_name = "ethtool", 992 | .m_doc = "Python ethtool module", 993 | .m_size = -1, 994 | .m_methods = PyEthModuleMethods, 995 | }; 996 | 997 | MODULE_INIT_FUNC(ethtool) 998 | { 999 | PyObject *m; 1000 | m = PyModule_Create(&moduledef); 1001 | if (m == NULL) 1002 | return NULL; 1003 | 1004 | // Prepare the ethtool.etherinfo class 1005 | if (PyType_Ready(&PyEtherInfo_Type) < 0) 1006 | return NULL; 1007 | 1008 | // Prepare the ethtool IPv6 and IPv4 address types 1009 | if (PyType_Ready(ðtool_netlink_ip_address_Type)) 1010 | return NULL; 1011 | 1012 | // Setup constants 1013 | /* Interface is up: */ 1014 | PyModule_AddIntConstant(m, "IFF_UP", IFF_UP); 1015 | /* Broadcast address valid: */ 1016 | PyModule_AddIntConstant(m, "IFF_BROADCAST", IFF_BROADCAST); 1017 | /* Turn on debugging: */ 1018 | PyModule_AddIntConstant(m, "IFF_DEBUG", IFF_DEBUG); 1019 | /* Is a loopback net: */ 1020 | PyModule_AddIntConstant(m, "IFF_LOOPBACK", IFF_LOOPBACK); 1021 | /* Is a point-to-point link: */ 1022 | PyModule_AddIntConstant(m, "IFF_POINTOPOINT", IFF_POINTOPOINT); 1023 | /* Avoid use of trailers: */ 1024 | PyModule_AddIntConstant(m, "IFF_NOTRAILERS", IFF_NOTRAILERS); 1025 | /* Resources allocated: */ 1026 | PyModule_AddIntConstant(m, "IFF_RUNNING", IFF_RUNNING); 1027 | /* No address resolution protocol: */ 1028 | PyModule_AddIntConstant(m, "IFF_NOARP", IFF_NOARP); 1029 | /* Receive all packets: */ 1030 | PyModule_AddIntConstant(m, "IFF_PROMISC", IFF_PROMISC); 1031 | /* Receive all multicast packets: */ 1032 | PyModule_AddIntConstant(m, "IFF_ALLMULTI", IFF_ALLMULTI); 1033 | /* Master of a load balancer: */ 1034 | PyModule_AddIntConstant(m, "IFF_MASTER", IFF_MASTER); 1035 | /* Slave of a load balancer: */ 1036 | PyModule_AddIntConstant(m, "IFF_SLAVE", IFF_SLAVE); 1037 | /* Supports multicast: */ 1038 | PyModule_AddIntConstant(m, "IFF_MULTICAST", IFF_MULTICAST); 1039 | /* Can set media type: */ 1040 | PyModule_AddIntConstant(m, "IFF_PORTSEL", IFF_PORTSEL); 1041 | /* Auto media select active: */ 1042 | PyModule_AddIntConstant(m, "IFF_AUTOMEDIA", IFF_AUTOMEDIA); 1043 | /* Dialup device with changing addresses: */ 1044 | PyModule_AddIntConstant(m, "IFF_DYNAMIC", IFF_DYNAMIC); 1045 | /* IPv4 interface: */ 1046 | PyModule_AddIntConstant(m, "AF_INET", AF_INET); 1047 | /* IPv6 interface: */ 1048 | PyModule_AddIntConstant(m, "AF_INET6", AF_INET6); 1049 | /* python-ethtool version: */ 1050 | PyModule_AddStringConstant(m, "version", "python-ethtool v" VERSION); 1051 | 1052 | Py_INCREF(&PyEtherInfo_Type); 1053 | PyModule_AddObject(m, "etherinfo", (PyObject *)&PyEtherInfo_Type); 1054 | 1055 | Py_INCREF(ðtool_netlink_ip_address_Type); 1056 | PyModule_AddObject(m, "NetlinkIPaddress", 1057 | (PyObject *)ðtool_netlink_ip_address_Type); 1058 | 1059 | return m; 1060 | } 1061 | -------------------------------------------------------------------------------- /python-ethtool/include/py3c/compat.h: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed under the MIT license 3 | 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2015, Red Hat, Inc. and/or its affiliates 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | #ifndef _PY3C_COMPAT_H_ 28 | #define _PY3C_COMPAT_H_ 29 | #include 30 | 31 | #if PY_MAJOR_VERSION >= 3 32 | 33 | /***** Python 3 *****/ 34 | 35 | #define IS_PY3 1 36 | 37 | /* Strings */ 38 | 39 | #define PyStr_Type PyUnicode_Type 40 | #define PyStr_Check PyUnicode_Check 41 | #define PyStr_CheckExact PyUnicode_CheckExact 42 | #define PyStr_FromString PyUnicode_FromString 43 | #define PyStr_FromStringAndSize PyUnicode_FromStringAndSize 44 | #define PyStr_FromFormat PyUnicode_FromFormat 45 | #define PyStr_FromFormatV PyUnicode_FromFormatV 46 | #define PyStr_AsString PyUnicode_AsUTF8 47 | #define PyStr_Concat PyUnicode_Concat 48 | #define PyStr_Format PyUnicode_Format 49 | #define PyStr_InternInPlace PyUnicode_InternInPlace 50 | #define PyStr_InternFromString PyUnicode_InternFromString 51 | #define PyStr_Decode PyUnicode_Decode 52 | 53 | #define PyStr_AsUTF8String PyUnicode_AsUTF8String // returns PyBytes 54 | #define PyStr_AsUTF8 PyUnicode_AsUTF8 55 | #define PyStr_AsUTF8AndSize PyUnicode_AsUTF8AndSize 56 | 57 | /* Ints */ 58 | 59 | #define PyInt_Type PyLong_Type 60 | #define PyInt_Check PyLong_Check 61 | #define PyInt_CheckExact PyLong_CheckExact 62 | #define PyInt_FromString PyLong_FromString 63 | #define PyInt_FromLong PyLong_FromLong 64 | #define PyInt_FromSsize_t PyLong_FromSsize_t 65 | #define PyInt_FromSize_t PyLong_FromSize_t 66 | #define PyInt_AsLong PyLong_AsLong 67 | #define PyInt_AS_LONG PyLong_AS_LONG 68 | #define PyInt_AsUnsignedLongLongMask PyLong_AsUnsignedLongLongMask 69 | #define PyInt_AsSsize_t PyLong_AsSsize_t 70 | 71 | /* Module init */ 72 | 73 | #define MODULE_INIT_FUNC(name) \ 74 | PyMODINIT_FUNC PyInit_ ## name(void); \ 75 | PyMODINIT_FUNC PyInit_ ## name(void) 76 | 77 | #else 78 | 79 | /***** Python 2 *****/ 80 | 81 | #define IS_PY3 0 82 | 83 | /* Strings */ 84 | 85 | #define PyStr_Type PyString_Type 86 | #define PyStr_Check PyString_Check 87 | #define PyStr_CheckExact PyString_CheckExact 88 | #define PyStr_FromString PyString_FromString 89 | #define PyStr_FromStringAndSize PyString_FromStringAndSize 90 | #define PyStr_FromFormat PyString_FromFormat 91 | #define PyStr_FromFormatV PyString_FromFormatV 92 | #define PyStr_AsString PyString_AsString 93 | #define PyStr_Format PyString_Format 94 | #define PyStr_InternInPlace PyString_InternInPlace 95 | #define PyStr_InternFromString PyString_InternFromString 96 | #define PyStr_Decode PyString_Decode 97 | 98 | static PyObject *PyStr_Concat(PyObject *left, PyObject *right) { 99 | PyObject *str = left; 100 | Py_INCREF(left); // reference to old left will be stolen 101 | PyString_Concat(&str, right); 102 | if (str) { 103 | return str; 104 | } else { 105 | return NULL; 106 | } 107 | } 108 | 109 | #define PyStr_AsUTF8String(str) (Py_INCREF(str), (str)) 110 | #define PyStr_AsUTF8 PyString_AsString 111 | #define PyStr_AsUTF8AndSize(pystr, sizeptr) \ 112 | ((*sizeptr=PyString_Size(pystr)), PyString_AsString(pystr)) 113 | 114 | #define PyBytes_Type PyString_Type 115 | #define PyBytes_Check PyString_Check 116 | #define PyBytes_CheckExact PyString_CheckExact 117 | #define PyBytes_FromString PyString_FromString 118 | #define PyBytes_FromStringAndSize PyString_FromStringAndSize 119 | #define PyBytes_FromFormat PyString_FromFormat 120 | #define PyBytes_FromFormatV PyString_FromFormatV 121 | #define PyBytes_Size PyString_Size 122 | #define PyBytes_GET_SIZE PyString_GET_SIZE 123 | #define PyBytes_AsString PyString_AsString 124 | #define PyBytes_AS_STRING PyString_AS_STRING 125 | #define PyBytes_AsStringAndSize PyString_AsStringAndSize 126 | #define PyBytes_Concat PyString_Concat 127 | #define PyBytes_ConcatAndDel PyString_ConcatAndDel 128 | #define _PyBytes_Resize _PyString_Resize 129 | 130 | /* Floats */ 131 | 132 | #define PyFloat_FromString(str) PyFloat_FromString(str, NULL) 133 | 134 | /* Module init */ 135 | 136 | #define PyModuleDef_HEAD_INIT 0 137 | 138 | typedef struct PyModuleDef { 139 | int m_base; 140 | const char* m_name; 141 | const char* m_doc; 142 | Py_ssize_t m_size; 143 | PyMethodDef *m_methods; 144 | } PyModuleDef; 145 | 146 | #define PyModule_Create(def) \ 147 | Py_InitModule3((def)->m_name, (def)->m_methods, (def)->m_doc) 148 | 149 | #define MODULE_INIT_FUNC(name) \ 150 | static PyObject *PyInit_ ## name(void); \ 151 | PyMODINIT_FUNC init ## name(void); \ 152 | PyMODINIT_FUNC init ## name(void) { PyInit_ ## name(); } \ 153 | static PyObject *PyInit_ ## name(void) 154 | 155 | 156 | #endif 157 | 158 | #endif 159 | -------------------------------------------------------------------------------- /python-ethtool/netlink-address.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 - 2013 Red Hat Inc. 3 | * 4 | * David Malcolm 5 | * 6 | * This application is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU General Public License as published by the Free 8 | * Software Foundation; version 2. 9 | * 10 | * This application is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * General Public License for more details. 14 | */ 15 | 16 | /* Python object corresponding to a (struct rtnl_addr) */ 17 | #include 18 | #include "include/py3c/compat.h" 19 | #include 20 | #include "structmember.h" 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include "etherinfo_struct.h" 27 | #include "etherinfo.h" 28 | 29 | 30 | /* IP Address parsing: */ 31 | static PyObject * 32 | PyNetlinkIPaddress_from_rtnl_addr(struct rtnl_addr *addr) 33 | { 34 | PyNetlinkIPaddress *py_obj; 35 | char buf[INET6_ADDRSTRLEN+1]; 36 | struct nl_addr *peer_addr = NULL, *brdcst = NULL; 37 | 38 | py_obj = PyObject_New(PyNetlinkIPaddress, 39 | ðtool_netlink_ip_address_Type); 40 | if (!py_obj) { 41 | return NULL; 42 | } 43 | 44 | /* Set IP address family. Only AF_INET and AF_INET6 is supported */ 45 | py_obj->family = rtnl_addr_get_family(addr); 46 | if (py_obj->family != AF_INET && py_obj->family != AF_INET6) { 47 | PyErr_SetString(PyExc_RuntimeError, 48 | "Only IPv4 (AF_INET) and IPv6 (AF_INET6) " 49 | "address types are supported"); 50 | goto error; 51 | } 52 | 53 | /* Set local IP address: */ 54 | memset(&buf, 0, sizeof(buf)); 55 | if (!inet_ntop(py_obj->family, 56 | nl_addr_get_binary_addr(rtnl_addr_get_local(addr)), 57 | buf, sizeof(buf))) { 58 | PyErr_SetFromErrno(PyExc_RuntimeError); 59 | goto error; 60 | } 61 | py_obj->local = PyStr_FromString(buf); 62 | if (!py_obj->local) { 63 | goto error; 64 | } 65 | 66 | /* Set peer IP address: */ 67 | memset(&buf, 0, sizeof(buf)); 68 | if ((peer_addr = rtnl_addr_get_peer(addr))) { 69 | nl_addr2str(peer_addr, buf, sizeof(buf)); 70 | py_obj->peer = PyStr_FromString(buf); 71 | if (!py_obj->local) { 72 | goto error; 73 | } 74 | } else { 75 | py_obj->peer = NULL; 76 | } 77 | 78 | /* Set IP address prefix length (netmask): */ 79 | py_obj->prefixlen = rtnl_addr_get_prefixlen(addr); 80 | 81 | /* Set ipv4_broadcast: */ 82 | py_obj->ipv4_broadcast = NULL; 83 | brdcst = rtnl_addr_get_broadcast(addr); 84 | if (py_obj->family == AF_INET && brdcst) { 85 | memset(&buf, 0, sizeof(buf)); 86 | if (!inet_ntop(AF_INET, nl_addr_get_binary_addr(brdcst), 87 | buf, sizeof(buf))) { 88 | PyErr_SetFromErrno(PyExc_RuntimeError); 89 | goto error; 90 | } 91 | py_obj->ipv4_broadcast = PyStr_FromString(buf); 92 | if (!py_obj->ipv4_broadcast) { 93 | goto error; 94 | } 95 | } 96 | 97 | /* Set IP address scope: */ 98 | memset(&buf, 0, sizeof(buf)); 99 | rtnl_scope2str(rtnl_addr_get_scope(addr), buf, sizeof(buf)); 100 | py_obj->scope = PyStr_FromString(buf); 101 | 102 | return (PyObject*)py_obj; 103 | 104 | error: 105 | Py_DECREF(py_obj); 106 | return NULL; 107 | } 108 | 109 | static void 110 | netlink_ip_address_dealloc(PyNetlinkIPaddress *obj) 111 | { 112 | Py_DECREF(obj->local); 113 | Py_XDECREF(obj->peer); 114 | Py_XDECREF(obj->ipv4_broadcast); 115 | Py_XDECREF(obj->scope); 116 | 117 | /* We can call PyObject_Del directly rather than calling through 118 | tp_free since the type is not subtypable (Py_TPFLAGS_BASETYPE is 119 | not set): */ 120 | PyObject_Del(obj); 121 | } 122 | 123 | static PyObject* 124 | netlink_ip_address_repr(PyNetlinkIPaddress *obj) 125 | { 126 | PyObject *result = PyStr_FromString("ethtool.NetlinkIPaddress(family="); 127 | char buf[256]; 128 | 129 | memset(&buf, 0, sizeof(buf)); 130 | nl_af2str(obj->family, buf, sizeof(buf)); 131 | result = PyStr_Concat(result, 132 | PyStr_FromFormat("%s, address='%s", 133 | buf, 134 | PyStr_AsString(obj->local))); 135 | 136 | if (obj->family == AF_INET) { 137 | result = PyStr_Concat(result, 138 | PyStr_FromFormat("', netmask=%d", 139 | obj->prefixlen)); 140 | } else if (obj->family == AF_INET6) { 141 | result = PyStr_Concat(result, 142 | PyStr_FromFormat("/%d'", obj->prefixlen)); 143 | } 144 | 145 | if (obj->peer) { 146 | result = PyStr_Concat(result, 147 | PyStr_FromFormat(", peer_address='%s'", 148 | PyStr_AsString(obj->peer))); 149 | } 150 | 151 | if (obj->family == AF_INET && obj->ipv4_broadcast) { 152 | result = PyStr_Concat(result, 153 | PyStr_FromFormat(", broadcast='%s'", 154 | PyStr_AsString( 155 | obj->ipv4_broadcast))); 156 | } 157 | 158 | result = PyStr_Concat(result, 159 | PyStr_FromFormat(", scope=%s)", 160 | PyStr_AsString(obj->scope))); 161 | 162 | return result; 163 | } 164 | 165 | 166 | static PyMemberDef _ethtool_netlink_ip_address_members[] = { 167 | { "address", 168 | T_OBJECT_EX, 169 | offsetof(PyNetlinkIPaddress, local), 170 | 0, 171 | NULL 172 | }, 173 | { "peer_address", 174 | T_OBJECT_EX, 175 | offsetof(PyNetlinkIPaddress, peer), 176 | 0, 177 | NULL 178 | }, 179 | { "netmask", 180 | T_INT, 181 | offsetof(PyNetlinkIPaddress, prefixlen), 182 | 0, 183 | NULL 184 | }, 185 | { "broadcast", 186 | T_OBJECT, /* can be NULL */ 187 | offsetof(PyNetlinkIPaddress, ipv4_broadcast), 188 | 0, 189 | NULL 190 | }, 191 | { "scope", 192 | T_OBJECT_EX, 193 | offsetof(PyNetlinkIPaddress, scope), 194 | 0, 195 | NULL 196 | }, 197 | {NULL} /* End of member list */ 198 | }; 199 | 200 | PyTypeObject ethtool_netlink_ip_address_Type = { 201 | PyVarObject_HEAD_INIT(0, 0) 202 | .tp_name = "ethtool.NetlinkIPaddress", 203 | .tp_basicsize = sizeof(PyNetlinkIPaddress), 204 | .tp_dealloc = (destructor)netlink_ip_address_dealloc, 205 | .tp_repr = (reprfunc)netlink_ip_address_repr, 206 | .tp_members = _ethtool_netlink_ip_address_members, 207 | }; 208 | 209 | 210 | PyObject * 211 | make_python_address_from_rtnl_addr(struct rtnl_addr *addr) 212 | { 213 | assert(addr); 214 | 215 | switch( rtnl_addr_get_family(addr)) { 216 | 217 | case AF_INET: 218 | case AF_INET6: 219 | return PyNetlinkIPaddress_from_rtnl_addr(addr); 220 | 221 | default: 222 | return PyErr_SetFromErrno(PyExc_RuntimeError); 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /python-ethtool/netlink.c: -------------------------------------------------------------------------------- 1 | /* netlink.c - Generic NETLINK API functions 2 | * 3 | * Copyright (C) 2009-2013 Red Hat Inc. 4 | * 5 | * David Sommerseth 6 | * 7 | * This application is free software; you can redistribute it and/or modify it 8 | * under the terms of the GNU General Public License as published by the Free 9 | * Software Foundation; version 2. 10 | * 11 | * This application is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * General Public License for more details. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "etherinfo_struct.h" 25 | 26 | pthread_mutex_t nlc_counter_mtx = PTHREAD_MUTEX_INITIALIZER; 27 | static struct nl_sock *nlconnection = NULL; 28 | 29 | /* How many NETLINK users are active? */ 30 | static unsigned int nlconnection_users = 0; 31 | 32 | 33 | /** 34 | * Connects to the NETLINK interface. This will be called 35 | * for each etherinfo object being generated, and it will 36 | * keep a separate file descriptor open for each object 37 | * 38 | * @param ethi PyEtherInfo structure (basically the "self" object) 39 | * 40 | * @return Returns 1 on success, otherwise 0. 41 | */ 42 | int open_netlink(PyEtherInfo *ethi) 43 | { 44 | if (!ethi) { 45 | return 0; 46 | } 47 | 48 | /* Reuse already established NETLINK connection, if a connection exists */ 49 | if (nlconnection) { 50 | /* If this object has not used NETLINK earlier, tag it as a user */ 51 | if (!ethi->nlc_active) { 52 | pthread_mutex_lock(&nlc_counter_mtx); 53 | nlconnection_users++; 54 | pthread_mutex_unlock(&nlc_counter_mtx); 55 | } 56 | ethi->nlc_active = 1; 57 | return 1; 58 | } 59 | 60 | /* No earlier connections exists, establish a new one */ 61 | nlconnection = nl_socket_alloc(); 62 | if (nlconnection != NULL) { 63 | if (nl_connect(nlconnection, NETLINK_ROUTE) < 0) { 64 | return 0; 65 | } 66 | /* Force O_CLOEXEC flag on the NETLINK socket */ 67 | if (fcntl(nl_socket_get_fd(nlconnection), F_SETFD, FD_CLOEXEC) == -1) { 68 | fprintf(stderr, 69 | "**WARNING** Failed to set O_CLOEXEC on NETLINK socket: " 70 | "%s\n", 71 | strerror(errno)); 72 | } 73 | 74 | /* Tag this object as an active user */ 75 | pthread_mutex_lock(&nlc_counter_mtx); 76 | nlconnection_users++; 77 | pthread_mutex_unlock(&nlc_counter_mtx); 78 | ethi->nlc_active = 1; 79 | return 1; 80 | } else { 81 | return 0; 82 | } 83 | } 84 | 85 | 86 | /** 87 | * Return a reference to the global netlink connection 88 | * 89 | * @returns Returns a pointer to a NETLINK connection libnl functions can use 90 | */ 91 | struct nl_sock * get_nlc() 92 | { 93 | assert(nlconnection); 94 | return nlconnection; 95 | } 96 | 97 | /** 98 | * Closes the NETLINK connection. This should be called automatically whenever 99 | * the corresponding etherinfo object is deleted. 100 | * 101 | * @param ethi PyEtherInfo structure (basically the "self" object) 102 | */ 103 | void close_netlink(PyEtherInfo *ethi) 104 | { 105 | if (!ethi || !nlconnection) { 106 | return; 107 | } 108 | 109 | /* Untag this object as a NETLINK user */ 110 | ethi->nlc_active = 0; 111 | pthread_mutex_lock(&nlc_counter_mtx); 112 | nlconnection_users--; 113 | pthread_mutex_unlock(&nlc_counter_mtx); 114 | 115 | /* Don't close the connection if there are more users */ 116 | if (nlconnection_users > 0) { 117 | return; 118 | } 119 | 120 | /* Close NETLINK connection */ 121 | nl_close(nlconnection); 122 | nl_socket_free(nlconnection); 123 | nlconnection = NULL; 124 | } 125 | -------------------------------------------------------------------------------- /scripts/pethtool: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python 2 | # -*- python -*- 3 | # -*- coding: utf-8 -*- 4 | # Copyright (C) 2008 Red Hat Inc. 5 | # 6 | # This application is free software; you can redistribute it and/or 7 | # modify it under the terms of the GNU General Public License 8 | # as published by the Free Software Foundation; version 2. 9 | # 10 | # This application is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | # General Public License for more details. 14 | 15 | from __future__ import unicode_literals, print_function 16 | 17 | import getopt 18 | import ethtool 19 | import sys 20 | 21 | 22 | def usage(): 23 | print('''Usage: pethtool [OPTIONS] [] 24 | -h|--help Give this help list 25 | -c|--show-coalesce Show coalesce options 26 | -C|--coalesce Set coalesce options 27 | [adaptive-rx on|off] 28 | [adaptive-tx on|off] 29 | [rx-usecs N] 30 | [rx-frames N] 31 | [rx-usecs-irq N] 32 | [rx-frames-irq N] 33 | [tx-usecs N] 34 | [tx-frames N] 35 | [tx-usecs-irq N] 36 | [tx-frames-irq N] 37 | [stats-block-usecs N] 38 | [pkt-rate-low N] 39 | [rx-usecs-low N] 40 | [rx-frames-low N] 41 | [tx-usecs-low N] 42 | [tx-frames-low N] 43 | [pkt-rate-high N] 44 | [rx-usecs-high N] 45 | [rx-frames-high N] 46 | [tx-usecs-high N] 47 | [tx-frames-high N] 48 | [sample-interval N] 49 | -g|--show-ring Show ring parameters 50 | -G|--set-ring Set ring parameters 51 | [rx N] 52 | [rx-mini N] 53 | [rx-jumbo N] 54 | [tx N] 55 | -i|--driver Show driver information 56 | -k|--show-offload Get protocol offload information 57 | -K|--offload Set protocol offload 58 | [ tso on|off ]''') 59 | 60 | 61 | tab = '' 62 | 63 | 64 | def printtab(msg): 65 | print(tab + msg) 66 | 67 | 68 | all_devices = [] 69 | 70 | ethtool_coalesce_msgs = ( 71 | ('stats-block-usecs', 72 | 'stats_block_coalesce_usecs'), 73 | ('sample-interval', 74 | 'rate_sample_interval'), 75 | ('pkt-rate-low', 76 | 'pkt_rate_low'), 77 | ('pkt-rate-high', 78 | 'pkt_rate_high'), 79 | ('\n'), 80 | ('rx-usecs', 81 | 'rx_coalesce_usecs'), 82 | ('rx-frames', 83 | 'rx_max_coalesced_frames'), 84 | ('rx-usecs-irq', 85 | 'rx_coalesce_usecs_irq'), 86 | ('rx-frames-irq', 87 | 'rx_max_coalesced_frames_irq'), 88 | ('\n'), 89 | ('tx-usecs', 90 | 'tx_coalesce_usecs'), 91 | ('tx-frames', 92 | 'tx_max_coalesced_frames'), 93 | ('tx-usecs-irq', 94 | 'tx_coalesce_usecs_irq'), 95 | ('tx-frames-irq', 96 | 'tx_max_coalesced_frames_irq'), 97 | ('\n'), 98 | ('rx-usecs-low', 99 | 'rx_coalesce_usecs_low'), 100 | ('rx-frame-low', 101 | 'rx_max_coalesced_frames_low'), 102 | ('tx-usecs-low', 103 | 'tx_coalesce_usecs_low'), 104 | ('tx-frame-low', 105 | 'tx_max_coalesced_frames_low'), 106 | ('\n'), 107 | ('rx-usecs-high', 108 | 'rx_coalesce_usecs_high'), 109 | ('rx-frame-high', 110 | 'rx_max_coalesced_frames_high'), 111 | ('tx-usecs-high', 112 | 'tx_coalesce_usecs_high'), 113 | ('tx-frame-high', 114 | 'tx_max_coalesced_frames_high'), 115 | ) 116 | 117 | 118 | def get_coalesce_dict_entry(ethtool_name): 119 | if ethtool_name == 'adaptive-rx': 120 | return 'use_adaptive_rx_coalesce' 121 | 122 | if ethtool_name == 'adaptive-tx': 123 | return 'use_adaptive_tx_coalesce' 124 | 125 | for name in ethtool_coalesce_msgs: 126 | if name[0] == ethtool_name: 127 | return name[1] 128 | 129 | return None 130 | 131 | 132 | def show_coalesce(interface, args=None): 133 | printtab('Coalesce parameters for %s:' % interface) 134 | try: 135 | coal = ethtool.get_coalesce(interface) 136 | except IOError: 137 | printtab(' NOT supported!') 138 | return 139 | 140 | printtab('Adaptive RX: %s TX: %s' % 141 | (coal['use_adaptive_rx_coalesce'] and 'on' or 'off', 142 | coal['use_adaptive_tx_coalesce'] and 'on' or 'off')) 143 | 144 | printed = ['use_adaptive_rx_coalesce', 145 | 'use_adaptive_tx_coalesce'] 146 | for tunable in ethtool_coalesce_msgs: 147 | if tunable[0] == '\n': 148 | print 149 | else: 150 | printtab('%s: %s' % (tunable[0], coal[tunable[1]])) 151 | printed.append(tunable[1]) 152 | 153 | coalkeys = coal.keys() 154 | if len(coalkeys) != len(printed): 155 | print 156 | for tunable in coalkeys: 157 | if tunable not in printed: 158 | printtab('%s %s' % (tunable, coal[tunable])) 159 | 160 | 161 | def set_coalesce(interface, args): 162 | try: 163 | coal = ethtool.get_coalesce(interface) 164 | except IOError: 165 | printtab('Interrupt coalescing NOT supported on %s!' % interface) 166 | return 167 | 168 | changed = False 169 | args = [a.lower() for a in args] 170 | for arg, value in [(args[i], args[i + 1]) for i in range(0, len(args), 2)]: 171 | real_arg = get_coalesce_dict_entry(arg) 172 | if not real_arg: 173 | continue 174 | if value == 'on': 175 | value = 1 176 | elif value == 'off': 177 | value = 0 178 | else: 179 | try: 180 | value = int(value) 181 | except: 182 | continue 183 | if coal[real_arg] != value: 184 | coal[real_arg] = value 185 | changed = True 186 | 187 | if not changed: 188 | return 189 | 190 | ethtool.set_coalesce(interface, coal) 191 | 192 | 193 | def show_offload(interface, args=None): 194 | try: 195 | sg = ethtool.get_sg(interface) and 'on' or 'off' 196 | except IOError: 197 | sg = 'not supported' 198 | 199 | try: 200 | tso = ethtool.get_tso(interface) and 'on' or 'off' 201 | except IOError: 202 | tso = 'not supported' 203 | 204 | try: 205 | ufo = ethtool.get_ufo(interface) and 'on' or 'off' 206 | except IOError: 207 | ufo = 'not supported' 208 | 209 | try: 210 | gso = ethtool.get_gso(interface) and 'on' or 'off' 211 | except IOError: 212 | gso = 'not supported' 213 | 214 | try: 215 | gro = ethtool.get_gro(interface) and 'on' or 'off' 216 | except IOError: 217 | gro = 'not supported' 218 | 219 | printtab('scatter-gather: %s' % sg) 220 | printtab('tcp segmentation offload: %s' % tso) 221 | printtab('udp fragmentation offload: %s' % ufo) 222 | printtab('generic segmentation offload: %s' % gso) 223 | printtab('generic receive offload: %s' % gro) 224 | 225 | 226 | def set_offload(interface, args): 227 | cmd, value = [a.lower() for a in args] 228 | 229 | if cmd == 'tso': 230 | value = value == 'on' and 1 or 0 231 | try: 232 | ethtool.set_tso(interface, value) 233 | except: 234 | pass 235 | elif cmd == 'gso': 236 | value = value == 'on' and 1 or 0 237 | try: 238 | ethtool.set_gso(interface, value) 239 | except: 240 | pass 241 | elif cmd == 'gro': 242 | value = value == 'on' and 1 or 0 243 | try: 244 | ethtool.set_gro(interface, value) 245 | except: 246 | pass 247 | 248 | 249 | ethtool_ringparam_msgs = ( 250 | ('Pre-set maximums', ), 251 | ('RX:\t\t', 'rx_max_pending'), 252 | ('RX Mini:\t', 'rx_mini_max_pending'), 253 | ('RX Jumbo:\t', 'rx_jumbo_max_pending'), 254 | ('TX:\t\t', 'tx_max_pending'), 255 | ('Current hardware settings', ), 256 | ('RX:\t\t', 'rx_pending'), 257 | ('RX Mini:\t', 'rx_mini_pending'), 258 | ('RX Jumbo:\t', 'rx_jumbo_pending'), 259 | ('TX:\t\t', 'tx_pending'), 260 | ) 261 | 262 | 263 | def show_ring(interface, args=None): 264 | printtab('Ring parameters for %s:' % interface) 265 | try: 266 | ring = ethtool.get_ringparam(interface) 267 | except IOError: 268 | printtab(' NOT supported!') 269 | return 270 | 271 | printed = [] 272 | for tunable in ethtool_ringparam_msgs: 273 | if len(tunable) == 1: 274 | printtab('%s:' % tunable[0]) 275 | else: 276 | printtab('%s %s' % (tunable[0], ring[tunable[1]])) 277 | printed.append(tunable[1]) 278 | 279 | ringkeys = ring.keys() 280 | if len(ringkeys) != len(printed): 281 | print 282 | for tunable in ringkeys: 283 | if tunable not in printed: 284 | printtab('%s %s' % (tunable, ring[tunable])) 285 | 286 | 287 | ethtool_ringparam_map = { 288 | 'rx': 'rx_pending', 289 | 'rx-mini': 'rx_mini_pending', 290 | 'rx-jumbo': 'rx_jumbo_pending', 291 | 'tx': 'tx_pending', 292 | } 293 | 294 | 295 | def set_ringparam(interface, args): 296 | try: 297 | ring = ethtool.get_ringparam(interface) 298 | except IOError: 299 | printtab('ring parameters NOT supported on %s!' % interface) 300 | return 301 | 302 | changed = False 303 | args = [a.lower() for a in args] 304 | for arg, value in [(args[i], args[i + 1]) for i in range(0, len(args), 2)]: 305 | if arg not in ethtool_ringparam_map: 306 | continue 307 | try: 308 | value = int(value) 309 | except: 310 | continue 311 | real_arg = ethtool_ringparam_map[arg] 312 | if ring[real_arg] != value: 313 | ring[real_arg] = value 314 | changed = True 315 | 316 | if not changed: 317 | return 318 | 319 | ethtool.set_ringparam(interface, ring) 320 | 321 | 322 | def show_driver(interface, args=None): 323 | try: 324 | driver = ethtool.get_module(interface) 325 | except IOError: 326 | driver = 'not implemented' 327 | 328 | try: 329 | bus = ethtool.get_businfo(interface) 330 | except IOError: 331 | bus = 'not available' 332 | 333 | printtab('driver: %s' % driver) 334 | printtab('bus-info: %s' % bus) 335 | 336 | 337 | def run_cmd(cmd, interface, args): 338 | global tab, all_devices 339 | 340 | active_devices = ethtool.get_active_devices() 341 | if not interface: 342 | tab = ' ' 343 | for interface in all_devices: 344 | inactive = ' (not active)' 345 | if interface in active_devices: 346 | inactive = '' 347 | print('%s%s:' % (interface, inactive)) 348 | cmd(interface, args) 349 | else: 350 | cmd(interface, args) 351 | 352 | 353 | def run_cmd_noargs(cmd, args): 354 | if args: 355 | run_cmd(cmd, args[0], None) 356 | else: 357 | global all_devices 358 | all_devices = ethtool.get_devices() 359 | run_cmd(cmd, None, None) 360 | 361 | 362 | def main(): 363 | global all_devices 364 | 365 | try: 366 | opts, args = getopt.getopt(sys.argv[1:], 367 | 'hcCgGikK', 368 | ('help', 369 | 'show-coalesce', 370 | 'coalesce', 371 | 'show-ring', 372 | 'set-ring', 373 | 'driver', 374 | 'show-offload', 375 | 'offload')) 376 | except getopt.GetoptError as err: 377 | usage() 378 | print(err) 379 | sys.exit(2) 380 | 381 | if not opts: 382 | usage() 383 | sys.exit(0) 384 | 385 | for o, a in opts: 386 | if o in ('-h', '--help'): 387 | usage() 388 | return 389 | elif o in ('-c', '--show-coalesce'): 390 | run_cmd_noargs(show_coalesce, args) 391 | break 392 | elif o in ('-i', '--driver'): 393 | run_cmd_noargs(show_driver, args) 394 | break 395 | elif o in ('-k', '--show-offload'): 396 | run_cmd_noargs(show_offload, args) 397 | break 398 | elif o in ('-g', '--show-ring'): 399 | run_cmd_noargs(show_ring, args) 400 | break 401 | elif o in ('-K', '--offload', 402 | '-C', '--coalesce', 403 | '-G', '--set-ring'): 404 | all_devices = ethtool.get_devices() 405 | if len(args) < 2: 406 | usage() 407 | sys.exit(1) 408 | 409 | if args[0] not in all_devices: 410 | interface = None 411 | else: 412 | interface = args[0] 413 | args = args[1:] 414 | 415 | if o in ('-K', '--offload'): 416 | cmd = set_offload 417 | elif o in ('-C', '--coalesce'): 418 | cmd = set_coalesce 419 | elif o in ('-G', '--set-ring'): 420 | cmd = set_ringparam 421 | 422 | run_cmd(cmd, interface, args) 423 | break 424 | 425 | 426 | if __name__ == '__main__': 427 | main() 428 | -------------------------------------------------------------------------------- /scripts/pifconfig: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python 2 | # -*- python -*- 3 | # -*- coding: utf-8 -*- 4 | # Copyright (C) 2008 Red Hat Inc. 5 | # 6 | # Arnaldo Carvalho de Melo 7 | # 8 | # This application is free software; you can redistribute it and/or 9 | # modify it under the terms of the GNU General Public License 10 | # as published by the Free Software Foundation; version 2. 11 | # 12 | # This application is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | # General Public License for more details. 16 | 17 | from __future__ import unicode_literals, print_function 18 | 19 | import ethtool 20 | import socket 21 | import struct 22 | import sys 23 | from optparse import OptionParser 24 | 25 | 26 | def bits2netmask(bits): 27 | mask = (1 << 32) - (1 << 32 >> bits) 28 | return socket.inet_ntoa(struct.pack(">L", mask)) 29 | 30 | 31 | def flags2str(flags): 32 | string = '' 33 | if flags & ethtool.IFF_UP: 34 | string += 'UP ' 35 | if flags & ethtool.IFF_BROADCAST: 36 | string += 'BROADCAST ' 37 | if flags & ethtool.IFF_DEBUG: 38 | string += 'DEBUG ' 39 | if flags & ethtool.IFF_LOOPBACK: 40 | string += 'LOOPBACK ' 41 | if flags & ethtool.IFF_POINTOPOINT: 42 | string += 'POINTOPOINT ' 43 | if flags & ethtool.IFF_NOTRAILERS: 44 | string += 'NOTRAILERS ' 45 | if flags & ethtool.IFF_RUNNING: 46 | string += 'RUNNING ' 47 | if flags & ethtool.IFF_NOARP: 48 | string += 'NOARP ' 49 | if flags & ethtool.IFF_PROMISC: 50 | string += 'PROMISC ' 51 | if flags & ethtool.IFF_ALLMULTI: 52 | string += 'ALLMULTI ' 53 | if flags & ethtool.IFF_MASTER: 54 | string += 'MASTER ' 55 | if flags & ethtool.IFF_SLAVE: 56 | string += 'SLAVE ' 57 | if flags & ethtool.IFF_MULTICAST: 58 | string += 'MULTICAST ' 59 | if flags & ethtool.IFF_PORTSEL: 60 | string += 'PORTSEL ' 61 | if flags & ethtool.IFF_AUTOMEDIA: 62 | string += 'AUTOMEDIA ' 63 | if flags & ethtool.IFF_DYNAMIC: 64 | string += 'DYNAMIC ' 65 | 66 | return string.strip() 67 | 68 | 69 | def show_config(device): 70 | flags = ethtool.get_flags(device) 71 | 72 | for info in ethtool.get_interfaces_info(device): 73 | print(device) 74 | if not (flags & ethtool.IFF_LOOPBACK): 75 | print('\tHWaddr %s' % ethtool.get_hwaddr(device)) 76 | 77 | for addr in info.get_ipv4_addresses(): 78 | print('\tinet addr:%s' % addr.address, end=" ") 79 | if not (flags & (ethtool.IFF_LOOPBACK | ethtool.IFF_POINTOPOINT)): 80 | print('Bcast:%s' % addr.broadcast, end=" ") 81 | print('Mask:%s' % bits2netmask(addr.netmask)) 82 | 83 | for addr in info.get_ipv6_addresses(): 84 | print('\tinet6 addr: %s/%s Scope: %s' 85 | % (addr.address, 86 | addr.netmask, 87 | addr.scope)) 88 | print('\t%s\n' % flags2str(flags)) 89 | print 90 | 91 | 92 | def main(): 93 | usage = 'usage: %prog [interface [interface [interface] ...]]' 94 | parser = OptionParser(usage=usage) 95 | (opts, args) = parser.parse_args() 96 | 97 | if args is None or len(args) == 0: 98 | sel_devs = ethtool.get_active_devices() 99 | else: 100 | sel_devs = args 101 | 102 | for device in sel_devs: 103 | try: 104 | show_config(device) 105 | except Exception as ex: 106 | print('** ERROR ** [Device %s]: %s' % (device, str(ex))) 107 | sys.exit(2) 108 | 109 | 110 | if __name__ == '__main__': 111 | main() 112 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | from __future__ import print_function 5 | 6 | from setuptools import setup, Extension 7 | import sys 8 | from io import open 9 | 10 | try: 11 | import commands 12 | except ImportError: 13 | import subprocess as commands 14 | 15 | version = '0.15' 16 | 17 | 18 | class PkgConfigExtension(Extension): 19 | '''Extension with lazy properties taken from pkg-config''' 20 | 21 | def __init__(self, *args, **kwargs): 22 | '''Behaves like Extension's __init__ but you should not use 23 | include_dirs, library_dirs and libraries arguments. 24 | 25 | Extra arguments: 26 | pkg : string 27 | The name of the pkg-config package to use for querying 28 | extra_libraris : [string] 29 | This will be added to the libraries attribute. Optional. 30 | ''' 31 | self._pkg = kwargs['pkg'] 32 | del kwargs['pkg'] 33 | if 'extra_libraries' in kwargs: 34 | self._extra_libraries = kwargs['extra_libraries'] 35 | del kwargs['extra_libraries'] 36 | else: 37 | self._extra_libraries = [] 38 | 39 | Extension.__init__(self, *args, **kwargs) 40 | 41 | try: 42 | # on Python 2 we need to delete those now 43 | del self.include_dirs 44 | del self.library_dirs 45 | del self.libraries 46 | except AttributeError: 47 | # on Python 3, that's not needed or possible 48 | pass 49 | 50 | @classmethod 51 | def _str2list(cls, pkgstr, onlystr): 52 | res = [] 53 | for l in pkgstr.split(" "): 54 | if l.find(onlystr) == 0: 55 | res.append(l.replace(onlystr, "", 1)) 56 | return res 57 | 58 | @classmethod 59 | def _run(cls, command_string): 60 | res, output = commands.getstatusoutput(command_string) 61 | if res != 0: 62 | print('Failed to query %s' % command_string) 63 | sys.exit(1) 64 | return output 65 | 66 | @property 67 | def include_dirs(self): 68 | includes = self._run('pkg-config --cflags-only-I %s' % self._pkg) 69 | return self._str2list(includes, '-I') 70 | 71 | @property 72 | def library_dirs(self): 73 | libdirs = self._run('pkg-config --libs-only-L %s' % self._pkg) 74 | return self._str2list(libdirs, '-L') 75 | 76 | @property 77 | def libraries(self): 78 | libs = self._run('pkg-config --libs-only-l %s' % self._pkg) 79 | return self._str2list(libs, '-l') + self._extra_libraries 80 | 81 | @include_dirs.setter 82 | def include_dirs(self, value): 83 | pass 84 | 85 | @library_dirs.setter 86 | def library_dirs(self, value): 87 | pass 88 | 89 | @libraries.setter 90 | def libraries(self, value): 91 | pass 92 | 93 | 94 | with open('README.rst', encoding='utf-8') as f: 95 | long_description = f.read() 96 | 97 | with open('CHANGES.rst', encoding='utf-8') as f: 98 | long_description += '\n\n' 99 | long_description += f.read() 100 | 101 | setup(name='ethtool', 102 | version=version, 103 | description='Python module to interface with ethtool', 104 | long_description=long_description, 105 | 106 | author='Harald Hoyer, Arnaldo Carvalho de Melo, David Sommerseth', 107 | author_email='davids@redhat.com', 108 | 109 | maintainer='Lumír Balhar, Miro Hrončok, Charalampos Stratakis, Sanqui', 110 | maintainer_email='python-maint@redhat.com', 111 | 112 | url='https://github.com/fedora-python/python-ethtool', 113 | license='GPL-2.0', 114 | keywords='network networking ethernet tool ethtool', 115 | 116 | classifiers=[ 117 | 'Development Status :: 7 - Inactive', 118 | 'Intended Audience :: Developers', 119 | 'Intended Audience :: System Administrators', 120 | 'Operating System :: POSIX :: Linux', 121 | 'License :: OSI Approved :: GNU General Public License v2 (GPLv2)', 122 | 'Programming Language :: Python', 123 | 'Programming Language :: Python :: 2', 124 | 'Programming Language :: Python :: 2.7', 125 | 'Programming Language :: Python :: 3', 126 | 'Programming Language :: Python :: 3.5', 127 | 'Programming Language :: Python :: 3.6', 128 | 'Programming Language :: Python :: 3.7', 129 | 'Programming Language :: Python :: 3.8', 130 | 'Programming Language :: Python :: 3.9', 131 | 'Programming Language :: Python :: 3.10', 132 | 'Programming Language :: Python :: Implementation :: CPython', 133 | 'Topic :: Software Development :: Libraries', 134 | 'Topic :: System :: Networking', 135 | ], 136 | 137 | scripts=[ 138 | 'scripts/pethtool', 'scripts/pifconfig' 139 | ], 140 | 141 | ext_modules=[ 142 | PkgConfigExtension( 143 | 'ethtool', 144 | sources=[ 145 | 'python-ethtool/ethtool.c', 146 | 'python-ethtool/etherinfo.c', 147 | 'python-ethtool/etherinfo_obj.c', 148 | 'python-ethtool/netlink.c', 149 | 'python-ethtool/netlink-address.c'], 150 | extra_compile_args=[ 151 | '-fno-strict-aliasing', '-Wno-unused-function'], 152 | define_macros=[('VERSION', '"%s"' % version)], 153 | pkg='libnl-3.0', 154 | extra_libraries=['nl-route-3'], 155 | ) 156 | ] 157 | ) 158 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-python/python-ethtool/dccdf657c34b2bdae7f1ac86a5497f533b386bd1/tests/__init__.py -------------------------------------------------------------------------------- /tests/parse_ifconfig.py: -------------------------------------------------------------------------------- 1 | # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2 | # 3 | # Copyright (c) 2012 Red Hat, Inc. All rights reserved. 4 | # 5 | # This copyrighted material is made available to anyone wishing 6 | # to use, modify, copy, or redistribute it subject to the terms 7 | # and conditions of the GNU General Public License version 2. 8 | # 9 | # This program is distributed in the hope that it will be 10 | # useful, but WITHOUT ANY WARRANTY; without even the implied 11 | # warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 12 | # PURPOSE. See the GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public 15 | # License along with this program; if not, write to the Free 16 | # Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 17 | # Boston, MA 02110-1301, USA. 18 | # 19 | # Author: Dave Malcolm 20 | # 21 | # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 22 | 23 | # Screenscraper for the output of "ifconfig" 24 | # The relevant sources for ifconfig can be found in e.g.: 25 | # net-tools-1.60/lib/interface.c 26 | # within ife_print_long() 27 | 28 | import os 29 | import re 30 | from subprocess import Popen, PIPE 31 | import unittest 32 | 33 | __all__ = ['IfConfig'] 34 | 35 | # any whitespace: 36 | ws = '\s+' 37 | 38 | # any non-whitespace, as a delimited group: 39 | group_nonws = '(\S+)' 40 | 41 | # a decimal number, as a delimited group: 42 | group_dec = '([0-9]+)' 43 | 44 | # hexadecimal number of the form "0xf2600000" 45 | group_o_hex = '0x([0-9a-f]+)' 46 | 47 | # hexadecimal number without the "0x" prefix, e.g. "f2600000" 48 | group_hex = '([0-9a-f]+)' 49 | 50 | dot = r'\.' 51 | 52 | dec = '[0-9]+' 53 | 54 | group_ip4addr = ('(' + dec + dot + 55 | dec + dot + 56 | dec + dot + 57 | dec + ')') 58 | 59 | 60 | def parse_ip4addr(addr): 61 | import socket 62 | return (socket.inet_aton(addr)) 63 | 64 | 65 | class IfConfig: 66 | """ 67 | Wrapper around a single invocation of "ifconfig" 68 | """ 69 | 70 | def __init__(self, stdout=None, debug=False): 71 | if stdout is not None: 72 | self.stdout = stdout 73 | self.stderr = '' 74 | else: 75 | env = os.environ.copy() 76 | env.update(LANG='C.utf8') 77 | p = Popen('ifconfig', stdout=PIPE, stderr=PIPE, 78 | env=env, universal_newlines=True) 79 | self.stdout, self.stderr = p.communicate() 80 | if self.stderr != '': 81 | raise ValueError( 82 | 'stderr from ifconfig was nonempty:\n%s' % self.stderr) 83 | if 0: 84 | print('self.stdout: %r' % self.stdout) 85 | self.devices = [] 86 | curdev = None 87 | for line in self.stdout.splitlines(): 88 | if 0: 89 | print('line: %r' % line) 90 | 91 | if line == '': 92 | continue 93 | 94 | # Start of a device entry, e.g: 95 | # 'lo: flags=73 mtu 16436' 96 | # old ifconfig format: 97 | # 'lo Link encap:Local Loopback' 98 | m = re.match(r'(\w+): (.+)', line) 99 | mo = re.match(r'(\w+)\s+(.+)', line) 100 | if m: 101 | self.oldFormat = False 102 | devname = m.group(1) 103 | curdev = Device(devname, debug) 104 | self.devices.append(curdev) 105 | curdev._parse_rest_of_first_line(m.group(2)) 106 | continue 107 | 108 | if mo: 109 | self.oldFormat = True 110 | devname = mo.group(1) 111 | curdev = Device(devname, debug) 112 | self.devices.append(curdev) 113 | curdev._parse_rest_of_first_line_old(mo.group(2)) 114 | continue 115 | 116 | # If we don't have current device yet, doesn't make sense to 117 | # read the rest of output lines 118 | if curdev is None: 119 | continue 120 | 121 | if self.oldFormat: 122 | curdev._parse_line_old(line) 123 | else: 124 | curdev._parse_line(line) 125 | 126 | def get_device_by_name(self, devname): 127 | for dev in self.devices: 128 | if dev.name == devname: 129 | return dev 130 | raise ValueError('device not found: %r' % devname) 131 | 132 | 133 | class Device: 134 | """ 135 | Wrapper around a device entry within the output of "ifconfig" 136 | """ 137 | 138 | def __init__(self, name, debug=False): 139 | self.name = name 140 | self.debug = debug 141 | 142 | self.flagsint = None 143 | self.flagsstr = None 144 | self.mtu = None 145 | self.metric = None 146 | self.outfill = None 147 | self.keepalive = None 148 | 149 | self.inet = None 150 | self.netmask = None 151 | self.broadcast = None 152 | self.destination = None 153 | 154 | self.inet6 = None 155 | self.prefixlen = None 156 | self.scopeid = None 157 | 158 | self.hwname = None 159 | self.hwaddr = None 160 | self.txqueuelen = None 161 | self.hwtitle = None 162 | 163 | self.rxpackets = None 164 | self.rxbytes = None 165 | 166 | self.rxerrors = None 167 | self.rxdropped = None 168 | self.rxoverruns = None 169 | self.rxframe = None 170 | 171 | self.rxcompressed = None 172 | 173 | self.txpackets = None 174 | self.txbytes = None 175 | 176 | self.txerrors = None 177 | self.txdropped = None 178 | self.txoverruns = None 179 | self.txcarrier = None 180 | self.txcollisions = None 181 | 182 | self.txcompressed = None 183 | 184 | self.interrupt = None 185 | self.baseaddr = None 186 | self.memstart = None 187 | self.memend = None 188 | self.dma = None 189 | 190 | def get_netmask_bits(self): 191 | # Convert a dotted netmask string to a bitcount int 192 | # e.g. from "255.255.252.0" to 22: 193 | if not self.netmask: 194 | return 0 195 | packed = parse_ip4addr(self.netmask) 196 | # count bits in "packed": 197 | result = 0 198 | for ch in packed: 199 | ch = ord(ch) if isinstance(ch, str) else ch 200 | while ch: 201 | if ch & 1: 202 | result += 1 203 | ch //= 2 204 | return result 205 | 206 | def __repr__(self): 207 | return ('Device(name=%(name)r, flagsint=%(flagsint)r, ' 208 | 'flagsstr=%(flagsstr)r, mtu=%(mtu)r)' 209 | % (self.__dict__)) 210 | 211 | def _debug(self, groups): 212 | if self.debug: 213 | print('groups: %r' % (groups,)) 214 | 215 | def _parse_rest_of_first_line(self, text): 216 | m = re.match(r'flags=([0-9]+)<(.*)> mtu ([0-9]+)(.*)', text) 217 | if not m: 218 | raise ValueError('unable to parse: %r' % text) 219 | self._debug(m.groups()) 220 | self.flagsint = int(m.group(1)) 221 | self.flagsstr = m.group(2) 222 | self.mtu = int(m.group(3)) 223 | 224 | # It might have " outfill %d keepalive %d": 225 | m = re.match(ws 226 | + 'outfill' + ws + group_dec + ws 227 | + 'keepalive' + ws + group_dec, 228 | m.group(4)) 229 | if m: 230 | self._debug(m.groups()) 231 | self.outfill = int(m.group(1)) 232 | self.keepalive = int(m.group(2)) 233 | 234 | def _parse_rest_of_first_line_old(self, text): 235 | m = re.match('Link encap:(\w+ ?\w*)\s*(HWaddr )?(\S*)', text) 236 | if not m: 237 | raise ValueError('unable to parse: %r' % text) 238 | self.hwtitle = m.group(1).strip() 239 | if m.group(2): 240 | self.hwaddr = m.group(3) 241 | 242 | def _parse_line(self, line): 243 | m = re.match(ws 244 | + 'inet ' + group_ip4addr + ws 245 | + 'netmask ' + group_ip4addr + '(.*)', 246 | line) 247 | if m: 248 | self._debug(m.groups()) 249 | self.inet = m.group(1) 250 | self.netmask = m.group(2) 251 | # "broadcast" and "destination" are both optional: 252 | line = m.group(3) 253 | m = re.match(ws + 'broadcast ' + group_ip4addr + '(.*)', line) 254 | if m: 255 | self.broadcast = m.group(1) 256 | line = m.group(2) 257 | 258 | m = re.match(ws + 'destination ' + group_nonws, line) 259 | if m: 260 | self.destination = m.group(1) 261 | return 262 | 263 | m = re.match(ws 264 | + 'inet6 ' + group_nonws + ws 265 | + 'prefixlen' + ws + group_dec + ws 266 | + 'scopeid ' + group_nonws, 267 | line) 268 | if m: 269 | self._debug(m.groups()) 270 | self.inet6 = m.group(1) 271 | self.prefixlen = int(m.group(2)) 272 | self.scopeid = m.group(3) 273 | return 274 | 275 | # e.g. (with hwaddr): 276 | # ' ether ff:00:11:22:33:42 txqueuelen 1000 (Ethernet)' 277 | m = re.match(ws 278 | + group_nonws + ws + group_nonws + ws 279 | + 'txqueuelen' + ws + group_dec + ws 280 | + '\((.*)\)', line) 281 | if m: 282 | self._debug(m.groups()) 283 | self.hwname = m.group(1) 284 | self.hwaddr = m.group(2) 285 | self.txqueuelen = int(m.group(3)) 286 | self.hwtitle = m.group(4) 287 | return 288 | 289 | # e.g. (without hwaddr): 290 | # ' loop txqueuelen 0 (Local Loopback)' 291 | m = re.match(ws 292 | + group_nonws + ws 293 | + 'txqueuelen' + ws + group_dec + ws 294 | + '\((.*)\)', line) 295 | if m: 296 | self._debug(m.groups()) 297 | self.hwname = m.group(1) 298 | self.hwaddr = None 299 | self.txqueuelen = int(m.group(2)) 300 | self.hwtitle = m.group(3) 301 | return 302 | 303 | # e.g. ' RX packets 5313261 bytes 6062336615 (5.6 GiB)' 304 | m = re.match(ws 305 | + 'RX packets ' + group_dec + ws 306 | + 'bytes ' + group_dec + ws 307 | + '\(.*\)', 308 | line) 309 | if m: 310 | self._debug(m.groups()) 311 | self.rxpackets = int(m.group(1)) 312 | self.rxbytes = int(m.group(2)) 313 | return 314 | 315 | # e.g. ' RX errors 0 dropped 0 overruns 0 frame 0' 316 | m = re.match(ws 317 | + 'RX errors ' + group_dec + ws 318 | + 'dropped ' + group_dec + ws 319 | + 'overruns ' + group_dec + ws 320 | + 'frame ' + group_dec, 321 | line) 322 | if m: 323 | self._debug(m.groups()) 324 | self.rxerrors = int(m.group(1)) 325 | self.rxdropped = int(m.group(2)) 326 | self.rxoverruns = int(m.group(3)) 327 | self.rxframe = int(m.group(4)) 328 | return 329 | 330 | # e.g. ' TX packets 2949524 bytes 344092625 (328.1 MiB)' 331 | m = re.match(ws 332 | + 'TX packets ' + group_dec + ws 333 | + 'bytes ' + group_dec + ws 334 | + '\(.*\)', 335 | line) 336 | if m: 337 | self._debug(m.groups()) 338 | self.txpackets = int(m.group(1)) 339 | self.txbytes = int(m.group(2)) 340 | return 341 | 342 | # e.g. ' TX errors 0 dropped 0 overruns 0 carrier 0 343 | # collisions 0' 344 | m = re.match(ws 345 | + 'TX errors ' + group_dec + ws 346 | + 'dropped ' + group_dec + ws 347 | + 'overruns ' + group_dec + ws 348 | + 'carrier ' + group_dec + ws 349 | + 'collisions ' + group_dec, 350 | line) 351 | if m: 352 | self._debug(m.groups()) 353 | self.txerrors = int(m.group(1)) 354 | self.txdropped = int(m.group(2)) 355 | self.txoverruns = int(m.group(3)) 356 | self.txcarrier = int(m.group(4)) 357 | self.txcollisions = int(m.group(5)) 358 | return 359 | 360 | # e.g.' device interrupt 20 memory 0xf2600000-f2620000 ' 361 | m = re.match(ws 362 | + 'device' + ws + '(.*)', 363 | line) 364 | if m: 365 | self._debug(m.groups()) 366 | line = m.group(1) 367 | m = re.match('interrupt ' + group_dec + ws + '(.*)', 368 | line) 369 | if m: 370 | self.interrupt = int(m.group(1)) 371 | line = m.group(2) 372 | 373 | m = re.match('base ' + group_o_hex + ws + '(.*)', 374 | line) 375 | if m: 376 | self.baseaddr = int(m.group(1), 16) 377 | line = m.group(2) 378 | 379 | m = re.match('memory ' + group_o_hex + '-' + 380 | group_hex + ws + '(.*)', 381 | line) 382 | if m: 383 | self.memstart = int(m.group(1), 16) 384 | self.memend = int(m.group(2), 16) 385 | line = m.group(3) 386 | 387 | m = re.match('dma ' + group_o_hex, 388 | line) 389 | if m: 390 | self.dma = int(m.group(1), 16) 391 | 392 | return 393 | 394 | raise ValueError('parser could not handle line: %r' % line) 395 | 396 | def _parse_line_old(self, line): 397 | m = re.match(ws 398 | + 'inet addr:' + group_ip4addr 399 | + '(.*)', 400 | line) 401 | if m: 402 | self._debug(m.groups()) 403 | self.inet = m.group(1) 404 | # "destination (P-t-P)" and "Bcast" are both optional: 405 | line = m.group(2) 406 | m = re.match(ws + 'P-t-P:' + group_ip4addr + '(.*)', line) 407 | if m: 408 | self.destination = m.group(1) 409 | line = m.group(2) 410 | 411 | m = re.match(ws + 'Bcast:' + group_ip4addr + '(.*)', line) 412 | if m: 413 | self.broadcast = m.group(1) 414 | line = m.group(2) 415 | 416 | m = re.match(ws + 'Mask:' + group_ip4addr, line) 417 | if m: 418 | self.netmask = m.group(1) 419 | return 420 | 421 | m = re.match(ws 422 | + 'inet6 addr: ' + group_nonws + '/' 423 | + group_dec + ws 424 | + 'Scope:' + group_nonws, 425 | line) 426 | if m: 427 | self._debug(m.groups()) 428 | self.inet6 = m.group(1) 429 | self.prefixlen = int(m.group(2)) 430 | self.scopeid = m.group(3) 431 | return 432 | 433 | # UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 434 | m = re.match(ws + '(.*)' 435 | + ' MTU:' + group_dec + ws 436 | + 'Metric:' + group_dec 437 | + '(.*)', line) 438 | if m: 439 | self._debug(m.groups()) 440 | self.flagsstr = m.group(1) 441 | self.mtu = int(m.group(2)) 442 | self.metric = int(m.group(3)) 443 | 444 | # It might have " Outfill:%d Keepalive:%d": 445 | m = re.match(ws 446 | + 'Outfill:' + group_dec + ws 447 | + 'Keepalive:' + group_dec, 448 | m.group(4)) 449 | if m: 450 | self._debug(m.groups()) 451 | self.outfill = int(m.group(1)) 452 | self.keepalive = int(m.group(2)) 453 | return 454 | 455 | # RX packets:4458926 errors:0 dropped:0 overruns:0 frame:0 456 | m = re.match(ws 457 | + 'RX packets:' + group_dec + ws 458 | + 'errors:' + group_dec + ws 459 | + 'dropped:' + group_dec + ws 460 | + 'overruns:' + group_dec + ws 461 | + 'frame:' + group_dec, 462 | line) 463 | if m: 464 | self._debug(m.groups()) 465 | self.rxpackets = int(m.group(1)) 466 | self.rxerrors = int(m.group(2)) 467 | self.rxdropped = int(m.group(3)) 468 | self.rxoverruns = int(m.group(4)) 469 | self.rxframe = int(m.group(5)) 470 | return 471 | 472 | # " compressed:%lu\n" 473 | m = re.match(ws + 'compressed:' + group_dec, line) 474 | if m: 475 | self.rxcompressed = int(m.group(1)) 476 | return 477 | 478 | # TX packets:3536982 errors:0 dropped:0 overruns:0 carrier:0 479 | m = re.match(ws 480 | + 'TX packets:' + group_dec + ws 481 | + 'errors:' + group_dec + ws 482 | + 'dropped:' + group_dec + ws 483 | + 'overruns:' + group_dec + ws 484 | + 'carrier:' + group_dec, 485 | line) 486 | if m: 487 | self._debug(m.groups()) 488 | self.txpackets = int(m.group(1)) 489 | self.txerrors = int(m.group(2)) 490 | self.txdropped = int(m.group(3)) 491 | self.txoverruns = int(m.group(4)) 492 | self.txcarrier = int(m.group(5)) 493 | return 494 | 495 | # " collisions:%lu compressed:%lu txqueuelen:%d " 496 | m = re.match(ws + 'collisions:' + group_dec + ws + '(.*)', line) 497 | if m: 498 | self._debug(m.groups()) 499 | self.txcollisions = int(m.group(1)) 500 | line = m.group(2) 501 | m = re.match('compressed:' + group_dec + ws + '(.*)', line) 502 | if m: 503 | self.txcompressed = int(m.group(1)) 504 | line = m.group(2) 505 | 506 | m = re.match('txqueuelen:' + group_dec, line) 507 | if m: 508 | self.txqueuelen = int(m.group(1)) 509 | return 510 | 511 | # RX bytes:3380060233 (3.1 GiB) TX bytes:713438255 (680.3 MiB) 512 | m = re.match(ws + 'RX bytes:' + group_dec + ws + '\(.*\)' + ws 513 | + 'TX bytes:' + group_dec + ws + '\(.*\)', 514 | line) 515 | if m: 516 | self._debug(m.groups()) 517 | self.rxbytes = int(m.group(1)) 518 | self.txbytes = int(m.group(2)) 519 | return 520 | 521 | # Interrupt:17 Memory:da000000-da012800 522 | m = re.match(ws + 'Interrupt:' + group_dec + '\s*(.*)', line) 523 | if m: 524 | self._debug(m.groups()) 525 | self.interrupt = int(m.group(1)) 526 | line = m.group(2) 527 | 528 | m = re.match('Base address:' + group_o_hex + '\s*(.*)', line) 529 | if m: 530 | self._debug(m.groups()) 531 | self.baseaddr = int(m.group(1), 16) 532 | line = m.group(2) 533 | 534 | m = re.match('Memory:' + group_hex + '-' + group_hex + '\s*(.*)', line) 535 | if m: 536 | self._debug(m.groups()) 537 | self.memstart = int(m.group(1), 16) 538 | self.memend = int(m.group(2), 16) 539 | line = m.group(3) 540 | 541 | m = re.match('DMA chan:' + group_hex, line) 542 | if m: 543 | self._debug(m.groups()) 544 | self.dma = int(m.group(1), 16) 545 | 546 | return 547 | 548 | # ifconfig = IfConfig() 549 | # for dev in ifconfig.devices: 550 | # print(dev) 551 | 552 | 553 | class ParserTests(unittest.TestCase): 554 | def test_full(self): 555 | # Parse a complex pre-canned output from ifconfig 556 | # This is the output of "ifconfig" on this machine, sanitized 557 | # to remove stuff that identifies it 558 | full = ''' 559 | eth1: flags=4163 mtu 1500 560 | inet 1.12.123.124 netmask 255.255.252.0 broadcast 1.12.123.255 561 | inet6 ffff::ffff:ffff:ffff:ffff prefixlen 64 scopeid 0x20 562 | ether ff:00:11:22:33:42 txqueuelen 1000 (Ethernet) 563 | RX packets 5329294 bytes 6074440831 (5.6 GiB) 564 | RX errors 0 dropped 0 overruns 0 frame 0 565 | TX packets 2955574 bytes 344607935 (328.6 MiB) 566 | TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 567 | ''' + ' device interrupt 20 memory 0xf2600000-f2620000 ' + ''' 568 | 569 | lo: flags=73 mtu 16436 570 | inet 127.0.0.1 netmask 255.0.0.0 571 | inet6 ::1 prefixlen 128 scopeid 0x10 572 | loop txqueuelen 0 (Local Loopback) 573 | RX packets 1562620 bytes 524530977 (500.2 MiB) 574 | RX errors 0 dropped 0 overruns 0 frame 0 575 | TX packets 1562620 bytes 524530977 (500.2 MiB) 576 | TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 577 | 578 | virbr0: flags=4163 mtu 1500 579 | inet 1.12.123.124 netmask 255.255.252.0 broadcast 1.12.123.255 580 | ether ff:00:11:22:33:42 txqueuelen 0 (Ethernet) 581 | RX packets 88730 bytes 5140566 (4.9 MiB) 582 | RX errors 0 dropped 0 overruns 0 frame 0 583 | TX packets 177583 bytes 244647206 (233.3 MiB) 584 | TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 585 | 586 | vnet0: flags=4163 mtu 1500 587 | inet6 ffff::ffff:ffff:ffff:ffff prefixlen 64 scopeid 0x20 588 | ether ff:00:11:22:33:42 txqueuelen 0 (Ethernet) 589 | RX packets 538 bytes 172743 (168.6 KiB) 590 | RX errors 0 dropped 0 overruns 0 frame 0 591 | TX packets 26593 bytes 1379054 (1.3 MiB) 592 | TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 593 | 594 | vnet1: flags=4163 mtu 1500 595 | inet6 ffff::ffff:ffff:ffff:ffff prefixlen 64 scopeid 0x20 596 | ether ff:00:11:22:33:42 txqueuelen 0 (Ethernet) 597 | RX packets 71567 bytes 5033151 (4.7 MiB) 598 | RX errors 0 dropped 0 overruns 0 frame 0 599 | TX packets 216553 bytes 200424748 (191.1 MiB) 600 | TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 601 | ''' 602 | ifconfig = IfConfig(stdout=full) 603 | self.assertEqual(len(ifconfig.devices), 5) 604 | 605 | # Verify eth1: 606 | eth1 = ifconfig.devices[0] 607 | # eth1: flags=4163 mtu 1500 608 | self.assertEqual(eth1.name, 'eth1') 609 | self.assertEqual(eth1.flagsint, 4163) 610 | self.assertEqual(eth1.flagsstr, 'UP,BROADCAST,RUNNING,MULTICAST') 611 | self.assertEqual(eth1.mtu, 1500) 612 | # inet 1.12.123.124 netmask 255.255.252.0 broadcast 1.12.123.255 613 | self.assertEqual(eth1.inet, '1.12.123.124') 614 | self.assertEqual(eth1.netmask, '255.255.252.0') 615 | self.assertEqual(eth1.broadcast, '1.12.123.255') 616 | # inet6 ffff::ffff:ffff:ffff:ffff prefixlen 64 scopeid 0x20 617 | self.assertEqual(eth1.inet6, 'ffff::ffff:ffff:ffff:ffff') 618 | self.assertEqual(eth1.prefixlen, 64) 619 | self.assertEqual(eth1.scopeid, '0x20') 620 | # ether ff:00:11:22:33:42 txqueuelen 1000 (Ethernet) 621 | self.assertEqual(eth1.hwname, 'ether') 622 | self.assertEqual(eth1.hwaddr, 'ff:00:11:22:33:42') 623 | self.assertEqual(eth1.txqueuelen, 1000) 624 | self.assertEqual(eth1.hwtitle, 'Ethernet') 625 | # RX packets 5329294 bytes 6074440831 (5.6 GiB) 626 | self.assertEqual(eth1.rxpackets, 5329294) 627 | self.assertEqual(eth1.rxbytes, 6074440831) 628 | # RX errors 0 dropped 0 overruns 0 frame 0 629 | self.assertEqual(eth1.rxerrors, 0) 630 | self.assertEqual(eth1.rxdropped, 0) 631 | self.assertEqual(eth1.rxoverruns, 0) 632 | self.assertEqual(eth1.rxframe, 0) 633 | # TX packets 2955574 bytes 344607935 (328.6 MiB) 634 | self.assertEqual(eth1.txpackets, 2955574) 635 | self.assertEqual(eth1.txbytes, 344607935) 636 | # TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 637 | self.assertEqual(eth1.txerrors, 0) 638 | self.assertEqual(eth1.txdropped, 0) 639 | self.assertEqual(eth1.txoverruns, 0) 640 | self.assertEqual(eth1.txcarrier, 0) 641 | self.assertEqual(eth1.txcollisions, 0) 642 | # device interrupt 20 memory 0xf2600000-f2620000 643 | self.assertEqual(eth1.interrupt, 20) 644 | 645 | # Verify lo: 646 | lo = ifconfig.devices[1] 647 | self.assertEqual(lo.name, 'lo') 648 | # lo: flags=73 mtu 16436 649 | self.assertEqual(lo.flagsint, 73) 650 | self.assertEqual(lo.flagsstr, 'UP,LOOPBACK,RUNNING') 651 | self.assertEqual(lo.mtu, 16436) 652 | # inet 127.0.0.1 netmask 255.0.0.0 653 | self.assertEqual(lo.inet, '127.0.0.1') 654 | self.assertEqual(lo.netmask, '255.0.0.0') 655 | self.assertEqual(lo.broadcast, None) 656 | # inet6 ::1 prefixlen 128 scopeid 0x10 657 | self.assertEqual(lo.inet6, '::1') 658 | self.assertEqual(lo.prefixlen, 128) 659 | self.assertEqual(lo.scopeid, '0x10') 660 | # loop txqueuelen 0 (Local Loopback) 661 | self.assertEqual(lo.hwname, 'loop') 662 | self.assertEqual(lo.txqueuelen, 0) 663 | self.assertEqual(lo.hwtitle, 'Local Loopback') 664 | # RX packets 1562620 bytes 524530977 (500.2 MiB) 665 | # RX errors 0 dropped 0 overruns 0 frame 0 666 | # TX packets 1562620 bytes 524530977 (500.2 MiB) 667 | # TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 668 | 669 | # Verify virbr0: 670 | virbr0 = ifconfig.devices[2] 671 | self.assertEqual(virbr0.name, 'virbr0') 672 | # virbr0: flags=4163 mtu 1500 673 | # inet 1.12.123.124 netmask 255.255.252.0 broadcast 1.12.123.255 674 | # ether ff:00:11:22:33:42 txqueuelen 0 (Ethernet) 675 | # RX packets 88730 bytes 5140566 (4.9 MiB) 676 | # RX errors 0 dropped 0 overruns 0 frame 0 677 | # TX packets 177583 bytes 244647206 (233.3 MiB) 678 | # TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 679 | 680 | # Verify vnet0: 681 | vnet0 = ifconfig.devices[3] 682 | self.assertEqual(vnet0.name, 'vnet0') 683 | # vnet0: flags=4163 mtu 1500 684 | # inet6 ffff::ffff:ffff:ffff:ffff prefixlen 64 scopeid 0x20 685 | # ether ff:00:11:22:33:42 txqueuelen 0 (Ethernet) 686 | # RX packets 538 bytes 172743 (168.6 KiB) 687 | # RX errors 0 dropped 0 overruns 0 frame 0 688 | # TX packets 26593 bytes 1379054 (1.3 MiB) 689 | # TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 690 | 691 | # Verify vnet1: 692 | vnet1 = ifconfig.devices[4] 693 | self.assertEqual(vnet1.name, 'vnet1') 694 | # vnet1: flags=4163 mtu 1500 695 | # inet6 ffff::ffff:ffff:ffff:ffff prefixlen 64 scopeid 0x20 696 | # ether ff:00:11:22:33:42 txqueuelen 0 (Ethernet) 697 | # RX packets 71567 bytes 5033151 (4.7 MiB) 698 | # RX errors 0 dropped 0 overruns 0 frame 0 699 | # TX packets 216553 bytes 200424748 (191.1 MiB) 700 | # TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 701 | 702 | fullOld = ''' 703 | eth0 Link encap:Ethernet HWaddr 00:11:22:33:44:55 704 | inet addr:1.12.123.124 Bcast:1.12.123.255 Mask:255.255.252.0 705 | inet6 addr: dddd::dddd:dddd:dddd:dddd:dddd:dddd/64 Scope:Site 706 | inet6 addr: eeee::eeee:eeee:eeee:eeee:eeee:eeee/64 Scope:Global 707 | inet6 addr: ffff::ffff:ffff:ffff:ffff/64 Scope:Link 708 | UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 709 | RX packets:4458926 errors:0 dropped:0 overruns:0 frame:0 710 | TX packets:3536982 errors:0 dropped:0 overruns:0 carrier:0 711 | collisions:0 txqueuelen:1000 712 | RX bytes:3380060233 (3.1 GiB) TX bytes:713438255 (680.3 MiB) 713 | Interrupt:17 Memory:da000000-da012800 714 | 715 | lo Link encap:Local Loopback 716 | inet addr:127.0.0.1 Mask:255.0.0.0 717 | inet6 addr: ::1/128 Scope:Host 718 | UP LOOPBACK RUNNING MTU:16436 Metric:1 719 | RX packets:805234 errors:0 dropped:0 overruns:0 frame:0 720 | TX packets:805234 errors:0 dropped:0 overruns:0 carrier:0 721 | collisions:0 txqueuelen:0 722 | RX bytes:132177529 (126.0 MiB) TX bytes:132177529 (126.0 MiB) 723 | ''' 724 | ifconfig = IfConfig(stdout=fullOld) 725 | self.assertEqual(len(ifconfig.devices), 2) 726 | 727 | # Verify eth0: 728 | eth0 = ifconfig.devices[0] 729 | # eth0 Link encap:Ethernet HWaddr 00:11:22:33:44:55 730 | self.assertEqual(eth0.name, 'eth0') 731 | self.assertEqual(eth0.hwtitle, 'Ethernet') 732 | self.assertEqual(eth0.hwaddr, '00:11:22:33:44:55') 733 | # inet addr:1.12.123.124 Bcast:1.12.123.255 Mask:255.255.252.0 734 | self.assertEqual(eth0.inet, '1.12.123.124') 735 | self.assertEqual(eth0.netmask, '255.255.252.0') 736 | self.assertEqual(eth0.broadcast, '1.12.123.255') 737 | # inet6 addr: dddd::dddd:dddd:dddd:dddd:dddd:dddd/64 Scope:Site 738 | # self.assertEqual(eth0.inet6, 'dddd::dddd:dddd:dddd:dddd:dddd:dddd') 739 | # self.assertEqual(eth0.prefixlen, 64) 740 | # self.assertEqual(eth0.scopeid, 'Site') 741 | # inet6 addr: eeee::eeee:eeee:eeee:eeee:eeee:eeee/64 Scope:Global 742 | # self.assertEqual(eth0.inet6, 'eeee::eeee:eeee:eeee:eeee:eeee:eeee') 743 | # self.assertEqual(eth0.prefixlen, 64) 744 | # self.assertEqual(eth0.scopeid, 'Global') 745 | # inet6 addr: ffff::ffff:ffff:ffff:ffff/64 Scope:Link 746 | self.assertEqual(eth0.inet6, 'ffff::ffff:ffff:ffff:ffff') 747 | self.assertEqual(eth0.prefixlen, 64) 748 | self.assertEqual(eth0.scopeid, 'Link') 749 | # UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 750 | self.assertEqual(eth0.flagsstr, 'UP BROADCAST RUNNING MULTICAST') 751 | self.assertEqual(eth0.mtu, 1500) 752 | self.assertEqual(eth0.metric, 1) 753 | # RX packets:4458926 errors:0 dropped:0 overruns:0 frame:0 754 | self.assertEqual(eth0.rxpackets, 4458926) 755 | self.assertEqual(eth0.rxerrors, 0) 756 | self.assertEqual(eth0.rxdropped, 0) 757 | self.assertEqual(eth0.rxoverruns, 0) 758 | self.assertEqual(eth0.rxframe, 0) 759 | # TX packets:3536982 errors:0 dropped:0 overruns:0 carrier:0 760 | self.assertEqual(eth0.txpackets, 3536982) 761 | self.assertEqual(eth0.txerrors, 0) 762 | self.assertEqual(eth0.txdropped, 0) 763 | self.assertEqual(eth0.txoverruns, 0) 764 | self.assertEqual(eth0.txcarrier, 0) 765 | # collisions:0 txqueuelen:1000 766 | self.assertEqual(eth0.txcollisions, 0) 767 | self.assertEqual(eth0.txqueuelen, 1000) 768 | # RX bytes:3380060233 (3.1 GiB) TX bytes:713438255 (680.3 MiB) 769 | self.assertEqual(eth0.rxbytes, 3380060233) 770 | self.assertEqual(eth0.txbytes, 713438255) 771 | # Interrupt:17 Memory:da000000-da012800 772 | self.assertEqual(eth0.interrupt, 17) 773 | self.assertEqual(eth0.memstart, 0xda000000) 774 | self.assertEqual(eth0.memend, 0xda012800) 775 | 776 | # Verify lo: 777 | lo = ifconfig.devices[1] 778 | # lo Link encap:Local Loopback 779 | self.assertEqual(lo.name, 'lo') 780 | self.assertEqual(lo.hwtitle, 'Local Loopback') 781 | self.assertEqual(lo.hwaddr, None) 782 | # inet addr:127.0.0.1 Mask:255.0.0.0 783 | self.assertEqual(lo.inet, '127.0.0.1') 784 | self.assertEqual(lo.netmask, '255.0.0.0') 785 | self.assertEqual(lo.broadcast, None) 786 | # inet6 addr: ::1/128 Scope:Host 787 | self.assertEqual(lo.inet6, '::1') 788 | self.assertEqual(lo.prefixlen, 128) 789 | self.assertEqual(lo.scopeid, 'Host') 790 | # UP LOOPBACK RUNNING MTU:16436 Metric:1 791 | self.assertEqual(lo.flagsstr, 'UP LOOPBACK RUNNING') 792 | self.assertEqual(lo.mtu, 16436) 793 | self.assertEqual(lo.metric, 1) 794 | # RX packets:805234 errors:0 dropped:0 overruns:0 frame:0 795 | self.assertEqual(lo.rxpackets, 805234) 796 | # TX packets:805234 errors:0 dropped:0 overruns:0 carrier:0 797 | self.assertEqual(lo.txpackets, 805234) 798 | # collisions:0 txqueuelen:0 799 | # RX bytes:132177529 (126.0 MiB) TX bytes:132177529 (126.0 MiB) 800 | self.assertEqual(lo.rxbytes, 132177529) 801 | self.assertEqual(lo.txbytes, 132177529) 802 | 803 | def parse_single_device(self, stdout, debug=False): 804 | ifconfig = IfConfig(stdout, debug) 805 | self.assertEqual(len(ifconfig.devices), 1) 806 | return ifconfig.devices[0] 807 | 808 | def test_parsing_flags_line(self): 809 | dev = self.parse_single_device( 810 | 'foo: flags=4163 mtu 1500\n') 811 | self.assertEqual(dev.name, 'foo') 812 | self.assertEqual(dev.flagsint, 4163) 813 | self.assertEqual(dev.flagsstr, 'UP,BROADCAST,RUNNING,MULTICAST') 814 | self.assertEqual(dev.mtu, 1500) 815 | 816 | dev = self.parse_single_device( 817 | 'foo: flags=4163 mtu 1500 ' 818 | 'outfill 32 keepalive 96\n') 819 | self.assertEqual(dev.mtu, 1500) 820 | self.assertEqual(dev.outfill, 32) 821 | self.assertEqual(dev.keepalive, 96) 822 | 823 | # old format 824 | dev = self.parse_single_device( 825 | 'foo Link encap:Ethernet HWaddr F0:F0:F0:F0:F0:F0\n' 826 | ' UP BROADCAST RUNNING MULTICAST MTU:500 Metric:2 ' 827 | 'Outfill:55 Keepalive:66\n') 828 | self.assertEqual(dev.flagsstr, 'UP BROADCAST RUNNING MULTICAST') 829 | self.assertEqual(dev.mtu, 500) 830 | self.assertEqual(dev.metric, 2) 831 | self.assertEqual(dev.outfill, 55) 832 | self.assertEqual(dev.keepalive, 66) 833 | 834 | def test_parsing_ip(self): 835 | dev = self.parse_single_device( 836 | 'foo: flags=4163 mtu 1500\n' 837 | ' inet 1.12.123.124 netmask 255.255.252.0 broadcast ' 838 | '1.12.123.255\n') 839 | self.assertEqual(dev.inet, '1.12.123.124') 840 | self.assertEqual(dev.netmask, '255.255.252.0') 841 | self.assertEqual(dev.get_netmask_bits(), 22) 842 | self.assertEqual(dev.broadcast, '1.12.123.255') 843 | self.assertEqual(dev.destination, None) 844 | 845 | dev = self.parse_single_device( 846 | 'lo: flags=73 mtu 16436\n' 847 | ' inet 127.0.0.1 netmask 255.0.0.0\n') 848 | self.assertEqual(dev.inet, '127.0.0.1') 849 | self.assertEqual(dev.netmask, '255.0.0.0') 850 | self.assertEqual(dev.get_netmask_bits(), 8) 851 | 852 | self.assertEqual(dev.broadcast, None) 853 | self.assertEqual(dev.destination, None) 854 | 855 | dev = self.parse_single_device( 856 | 'foo: flags=4163 mtu 1500\n' 857 | ' inet 1.1.1.1 netmask 255.255.255.254 destination ' 858 | '1.12.123.255\n') 859 | self.assertEqual(dev.inet, '1.1.1.1') 860 | self.assertEqual(dev.netmask, '255.255.255.254') 861 | self.assertEqual(dev.broadcast, None) 862 | self.assertEqual(dev.destination, '1.12.123.255') 863 | 864 | # old format 865 | dev = self.parse_single_device( 866 | 'foo Link encap:Ethernet HWaddr F0:F0:F0:F0:F0:F0\n' 867 | ' inet addr:1.2.3.4 P-t-P:10.20.30.40 ' 868 | 'Mask:255.255.254.0\n') 869 | self.assertEqual(dev.inet, '1.2.3.4') 870 | self.assertEqual(dev.destination, '10.20.30.40') 871 | self.assertEqual(dev.broadcast, None) 872 | self.assertEqual(dev.get_netmask_bits(), 23) 873 | self.assertEqual(dev.netmask, '255.255.254.0') 874 | 875 | def test_parsing_device_line(self): 876 | dev = self.parse_single_device( 877 | 'foo: flags=4163 mtu 1500\n' 878 | ' device interrupt 20 memory 0xf2600000-f2620000 \n') 879 | self.assertEqual(dev.interrupt, 20) 880 | self.assertEqual(dev.baseaddr, None) 881 | self.assertEqual(dev.memstart, 0xf2600000) 882 | self.assertEqual(dev.memend, 0xf2620000) 883 | self.assertEqual(dev.dma, None) 884 | 885 | # but the fields in the "device" field are optional: 886 | dev = self.parse_single_device( 887 | 'foo: flags=4163 mtu 1500\n' 888 | ' device \n') 889 | self.assertEqual(dev.interrupt, None) 890 | self.assertEqual(dev.baseaddr, None) 891 | self.assertEqual(dev.memstart, None) 892 | self.assertEqual(dev.memend, None) 893 | self.assertEqual(dev.dma, None) 894 | 895 | dev = self.parse_single_device( 896 | 'foo: flags=4163 mtu 1500\n' 897 | ' device memory 0xf2600000-f2620000 \n') 898 | self.assertEqual(dev.interrupt, None) 899 | self.assertEqual(dev.baseaddr, None) 900 | self.assertEqual(dev.memstart, 0xf2600000) 901 | self.assertEqual(dev.memend, 0xf2620000) 902 | self.assertEqual(dev.dma, None) 903 | 904 | # and there could potentially be "base" and "dma" fields: 905 | dev = self.parse_single_device( 906 | 'foo: flags=4163 mtu 1500\n' 907 | ' device base 0xdeadbeef \n') 908 | self.assertEqual(dev.interrupt, None) 909 | self.assertEqual(dev.baseaddr, 0xdeadbeef) 910 | self.assertEqual(dev.memstart, None) 911 | self.assertEqual(dev.memend, None) 912 | self.assertEqual(dev.dma, None) 913 | 914 | dev = self.parse_single_device( 915 | 'foo: flags=4163 mtu 1500\n' 916 | ' device interrupt 20 dma 0xbad4f00d\n') 917 | self.assertEqual(dev.interrupt, 20) 918 | self.assertEqual(dev.baseaddr, None) 919 | self.assertEqual(dev.memstart, None) 920 | self.assertEqual(dev.memend, None) 921 | self.assertEqual(dev.dma, 0xbad4f00d) 922 | 923 | # old format 924 | dev = self.parse_single_device( 925 | 'foo Link encap:Ethernet HWaddr F0:F0:F0:F0:F0:F0\n' 926 | ' Interrupt:20 Base address:0xdeadbeef DMA chan:bad4f00d\n' 927 | ' collisions:13 compressed:11 \n') 928 | self.assertEqual(dev.interrupt, 20) 929 | self.assertEqual(dev.baseaddr, 0xdeadbeef) 930 | self.assertEqual(dev.dma, 0xbad4f00d) 931 | self.assertEqual(dev.txcollisions, 13) 932 | self.assertEqual(dev.txcompressed, 11) 933 | 934 | def test_parse_ip4addr(self): 935 | self.assertEqual(parse_ip4addr('1.1.1.1'), b'\x01\x01\x01\x01') 936 | self.assertEqual(parse_ip4addr('127.0.0.1'), b'\x7f\x00\x00\x01') 937 | 938 | def test_local(self): 939 | # Actually invoke ifconfig locally, and parse whatever it emits: 940 | IfConfig() 941 | 942 | 943 | if __name__ == '__main__': 944 | unittest.main() 945 | -------------------------------------------------------------------------------- /tests/test_ethtool.py: -------------------------------------------------------------------------------- 1 | # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2 | # 3 | # Copyright (c) 2011, 2012, 2013 Red Hat, Inc. All rights reserved. 4 | # 5 | # This copyrighted material is made available to anyone wishing 6 | # to use, modify, copy, or redistribute it subject to the terms 7 | # and conditions of the GNU General Public License version 2. 8 | # 9 | # This program is distributed in the hope that it will be 10 | # useful, but WITHOUT ANY WARRANTY; without even the implied 11 | # warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 12 | # PURPOSE. See the GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public 15 | # License along with this program; if not, write to the Free 16 | # Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 17 | # Boston, MA 02110-1301, USA. 18 | # 19 | # Author: Dave Malcolm 20 | # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 21 | 22 | import unittest 23 | 24 | import ethtool 25 | 26 | from .parse_ifconfig import IfConfig 27 | 28 | INVALID_DEVICE_NAME = "I am not a valid device name" 29 | 30 | # Screenscrape the output from "ifconfig". We will use this below to validate 31 | # the results from the "ethtool" module: 32 | ifconfig = IfConfig() 33 | for dev in ifconfig.devices: 34 | print(dev) 35 | 36 | 37 | class EthtoolTests(unittest.TestCase): 38 | 39 | # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 40 | # asserts 41 | # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 42 | 43 | def assertIsString(self, value): 44 | self.assertTrue(isinstance(value, str)) 45 | 46 | def assertIsStringOrNone(self, value): 47 | if value is not None: 48 | self.assertTrue(isinstance(value, str)) 49 | 50 | def assertIsInt(self, value): 51 | self.assertTrue(isinstance(value, int)) 52 | 53 | def assertRaisesIOError(self, fn, args, errmsg): 54 | """ 55 | Verify that an IOError is raised, and that the errno and message are 56 | as expected 57 | 58 | (Python 2.6 and earlier's assertRaises doesn't allow us to check the 59 | details of the exception that is raised) 60 | """ 61 | try: 62 | fn(*args) 63 | except IOError as e: 64 | # Check the details of the exception: 65 | enum, emsg = e.args 66 | self.assertEqual('[Errno {}] {}'.format(enum, emsg), errmsg) 67 | else: 68 | self.fail('IOError was not raised calling %s on %s' % (fn, args)) 69 | 70 | def assertRaisesNoSuchDevice(self, fn, *args): 71 | self.assertRaisesIOError(fn, args, '[Errno 19] No such device') 72 | 73 | def assertIsStringExceptForLoopback(self, fn, devname, errmsg): 74 | if devname == 'lo': 75 | self.assertRaisesIOError(fn, (devname, ), errmsg) 76 | else: 77 | self.assertIsString(fn(devname)) 78 | 79 | def assertEqualIpv4Str(self, ethtooladdr, scrapedaddr): 80 | if scrapedaddr is None: 81 | self.assertEqual(ethtooladdr, '0.0.0.0') 82 | else: 83 | self.assertEqual(ethtooladdr, scrapedaddr) 84 | 85 | def assertEqualHwAddr(self, ethtooladdr, scrapedaddr): 86 | if scrapedaddr is None: 87 | self.assertEqual(ethtooladdr, '00:00:00:00:00:00') 88 | else: 89 | self.assertEqual(ethtooladdr, scrapedaddr.lower()) 90 | 91 | # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 92 | # helpers 93 | # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 94 | 95 | def _functions_accepting_devnames(self, devname): 96 | self.assertIsString(devname) 97 | 98 | scraped = ifconfig.get_device_by_name(devname) 99 | 100 | try: 101 | broadcast = ethtool.get_broadcast(devname) 102 | except (OSError, IOError): 103 | # Some devices might end up with 104 | # [Errno 99] Cannot assign requested address 105 | # That's IOError on 2.7, OSError on 3 106 | assignable = False 107 | else: 108 | assignable = True 109 | self.assertIsString(broadcast) 110 | 111 | # Broadcast is optional in ifconfig output 112 | if scraped.broadcast: 113 | self.assertEqualIpv4Str(broadcast, scraped.broadcast) 114 | 115 | self.assertIsStringExceptForLoopback(ethtool.get_businfo, devname, 116 | '[Errno 95] Operation not ' 117 | 'supported') 118 | 119 | self.assertIsInt(ethtool.get_flags(devname)) 120 | # flagsint cannot be obtained from old ifconfig format 121 | if not ifconfig.oldFormat: 122 | self.assertEqual(ethtool.get_flags(devname), scraped.flagsint) 123 | self.assertIsInt(ethtool.get_gso(devname)) 124 | self.assertIsInt(ethtool.get_gro(devname)) 125 | self.assertIsString(ethtool.get_hwaddr(devname)) 126 | self.assertEqualHwAddr(ethtool.get_hwaddr(devname), 127 | scraped.hwaddr) 128 | 129 | if assignable: 130 | self.assertIsString(ethtool.get_ipaddr(devname)) 131 | self.assertEqual(ethtool.get_ipaddr(devname), scraped.inet) 132 | 133 | self.assertIsStringExceptForLoopback(ethtool.get_module, devname, 134 | '[Errno 95] Operation not ' 135 | 'supported') 136 | 137 | if assignable: 138 | self.assertIsString(ethtool.get_netmask(devname)) 139 | self.assertEqual(ethtool.get_netmask(devname), 140 | scraped.netmask) 141 | 142 | # Operation is not supported only on loopback device 143 | if devname == 'lo': 144 | self.assertRaisesIOError(ethtool.get_ringparam, (devname, ), 145 | '[Errno 95] Operation not supported') 146 | 147 | self.assertIsInt(ethtool.get_sg(devname)) 148 | 149 | try: 150 | self.assertIsInt(ethtool.get_ufo(devname)) 151 | except (OSError, IOError): 152 | # This test may fail due to insufficient privileges 153 | # That's IOError on 2.7, OSError (PermissionError) on 3 154 | pass 155 | 156 | self.assertIsInt(ethtool.get_tso(devname)) 157 | 158 | # TODO: self.assertIsString(ethtool.set_coalesce(devname)) 159 | 160 | # TODO: self.assertIsString(ethtool.set_ringparam(devname)) 161 | 162 | # TODO: self.assertIsString(ethtool.set_tso(devname)) 163 | 164 | # TODO: self.assertIsString(ethtool.set_gso(devname)) 165 | 166 | # TODO: self.assertIsString(ethtool.set_gro(devname)) 167 | 168 | def _verify_etherinfo_object(self, ei): 169 | self.assertTrue(isinstance(ei, ethtool.etherinfo)) 170 | self.assertIsString(ei.device) 171 | 172 | try: 173 | scraped = ifconfig.get_device_by_name(ei.device) 174 | except ValueError: 175 | scraped = None 176 | 177 | self.assertIsStringOrNone(ei.ipv4_address) 178 | if scraped and scraped.inet: 179 | addresses = [ip.address for ip in ei.get_ipv4_addresses()] 180 | self.assertTrue(scraped.inet in addresses) 181 | 182 | self.assertIsStringOrNone(ei.ipv4_broadcast) 183 | if scraped and scraped.broadcast not in (None, '0.0.0.0'): 184 | # Broadcast is optional 185 | broadcasts = [ip.broadcast for ip in ei.get_ipv4_addresses()] 186 | self.assertTrue(scraped.broadcast in broadcasts) 187 | 188 | self.assertIsInt(ei.ipv4_netmask) 189 | if scraped and scraped.netmask: 190 | netmasks = [ip.netmask for ip in ei.get_ipv4_addresses()] 191 | self.assertTrue(scraped.get_netmask_bits(), netmasks) 192 | 193 | self.assertIsStringOrNone(ei.mac_address) 194 | if scraped and scraped.hwaddr and scraped.hwtitle.lower() != 'unspec': 195 | scraped.hwaddr = scraped.hwaddr.lower() 196 | self.assertEqualHwAddr(ei.mac_address.lower(), scraped.hwaddr) 197 | 198 | i6s = ei.get_ipv6_addresses() 199 | for i6 in i6s: 200 | self.assertTrue(isinstance(i6, ethtool.NetlinkIPaddress)) 201 | self.assertIsString(i6.address) 202 | self.assertIsInt(i6.netmask) 203 | self.assertIsString(i6.scope) 204 | 205 | # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 206 | # tests 207 | # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 208 | 209 | def test_invalid_devices(self): 210 | # Verify sane handling of non-existant devices 211 | 212 | get_fns = ('get_broadcast', 'get_businfo', 'get_coalesce', 'get_flags', 213 | 'get_gso', 'get_gso', 'get_hwaddr', 'get_ipaddr', 214 | 'get_module', 'get_netmask', 'get_ringparam', 'get_sg', 215 | 'get_tso', 'get_ufo') 216 | for fnname in get_fns: 217 | self.assertRaisesNoSuchDevice(getattr(ethtool, fnname), 218 | INVALID_DEVICE_NAME) 219 | 220 | set_fns = ('set_coalesce', 'set_ringparam', 'set_tso', 'set_gso', 221 | 'set_gro') 222 | for fnname in set_fns: 223 | # Currently this fails, with an IOError from 224 | # ethtool.c:__struct_desc_from_dict 225 | # with message: 226 | # 'Missing dict entry for field rx_coalesce_usecs' 227 | if False: 228 | self.assertRaisesNoSuchDevice(getattr(ethtool, fnname), 229 | INVALID_DEVICE_NAME, 42) 230 | 231 | def test_get_interface_info_invalid(self): 232 | eis = ethtool.get_interfaces_info(INVALID_DEVICE_NAME) 233 | self.assertEqual(len(eis), 1) 234 | ei = eis[0] 235 | self.assertEqual(ei.device, INVALID_DEVICE_NAME) 236 | self.assertRaisesIOError( 237 | getattr, (ei, 'ipv4_address'), '[Errno 19] No such device') 238 | self.assertRaisesIOError( 239 | getattr, (ei, 'ipv4_netmask'), '[Errno 19] No such device') 240 | self.assertRaisesIOError( 241 | getattr, (ei, 'ipv4_broadcast'), '[Errno 19] No such device') 242 | self.assertRaisesIOError( 243 | getattr, (ei, 'mac_address'), '[Errno 19] No such device') 244 | 245 | def test_get_interface_info_active(self): 246 | eis = ethtool.get_interfaces_info(ethtool.get_active_devices()) 247 | for ei in eis: 248 | self._verify_etherinfo_object(ei) 249 | 250 | def test_get_interface_info_all(self): 251 | eis = ethtool.get_interfaces_info(ethtool.get_devices()) 252 | for ei in eis: 253 | self._verify_etherinfo_object(ei) 254 | 255 | def test_get_active_devices(self): 256 | for devname in ethtool.get_active_devices(): 257 | # Skip these test on tun and wg devices 258 | if devname.startswith('tun') or devname.startswith('wg'): 259 | continue 260 | self._functions_accepting_devnames(devname) 261 | 262 | def test_etherinfo_objects(self): 263 | devnames = ethtool.get_devices() 264 | eis = ethtool.get_interfaces_info(devnames) 265 | for ei in eis: 266 | self._verify_etherinfo_object(ei) 267 | 268 | 269 | if __name__ == '__main__': 270 | unittest.main() 271 | -------------------------------------------------------------------------------- /tests/test_scripts.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import ethtool 3 | import sys 4 | import os 5 | from io import TextIOWrapper, BytesIO 6 | 7 | try: 8 | from imp import load_source 9 | except ImportError: 10 | import importlib.machinery 11 | import importlib.util 12 | 13 | # from https://docs.python.org/3.12/whatsnew/3.12.html#imp 14 | def load_source(modname, filename): 15 | loader = importlib.machinery.SourceFileLoader(modname, filename) 16 | spec = importlib.util.spec_from_file_location(modname, filename, loader=loader) 17 | module = importlib.util.module_from_spec(spec) 18 | # The module is always executed and not cached in sys.modules. 19 | # Uncomment the following line to cache the module. 20 | # sys.modules[module.__name__] = module 21 | loader.exec_module(module) 22 | return module 23 | 24 | pifc = load_source('pifc', 'scripts/pifconfig') 25 | peth = load_source('peth', 'scripts/pethtool') 26 | 27 | 28 | def find_suitable_device(): 29 | active_devices = ethtool.get_active_devices() 30 | for device in active_devices: 31 | device_flags = ethtool.get_flags(device) 32 | str_flags = pifc.flags2str(device_flags) 33 | try: 34 | wireless_protocol = ethtool.get_wireless_protocol(device) 35 | except Exception: 36 | wireless_protocol = None 37 | 38 | if 'UP' in str_flags and 'RUNNING' in str_flags \ 39 | and 'BROADCAST' in str_flags and not wireless_protocol: 40 | return device 41 | 42 | return None 43 | 44 | 45 | loopback = 'lo' 46 | device = find_suitable_device() 47 | 48 | 49 | class ScriptsTests(unittest.TestCase): 50 | 51 | def setUp(self): 52 | self._old_stdout = sys.stdout 53 | self._stdout = sys.stdout = TextIOWrapper(BytesIO(), sys.stdout.encoding) 54 | 55 | def _output(self): 56 | self._stdout.seek(0) 57 | return self._stdout.read() 58 | 59 | def tearDown(self): 60 | sys.stdout = self._old_stdout 61 | self._stdout.close() 62 | 63 | def test_flags2str(self): 64 | self.assertEqual(pifc.flags2str(0), '') 65 | self.assertEqual(pifc.flags2str(73), 'UP LOOPBACK RUNNING') 66 | self.assertEqual(pifc.flags2str(4305), 'UP POINTOPOINT RUNNING NOARP MULTICAST') 67 | self.assertEqual(pifc.flags2str(4163), 'UP BROADCAST RUNNING MULTICAST') 68 | 69 | # Tests for loopback 70 | 71 | def test_driver_lo(self): 72 | self.assertIsNone(peth.show_driver(loopback)) 73 | self.assertEqual(self._output(), 74 | 'driver: not implemented\nbus-info: not available\n' 75 | ) 76 | 77 | def test_show_ring_lo(self): 78 | self.assertIsNone(peth.show_ring(loopback)) 79 | self.assertEqual(self._output(), 80 | 'Ring parameters for {}:\n NOT supported!\n'.format(loopback) 81 | ) 82 | 83 | def test_show_coalesce_lo(self): 84 | self.assertIsNone(peth.show_coalesce(loopback)) 85 | self.assertEqual(self._output(), 86 | 'Coalesce parameters for {}:\n NOT supported!\n'.format(loopback) 87 | ) 88 | 89 | def test_show_offload_lo(self): 90 | self.assertIsNone(peth.show_offload(loopback)) 91 | 92 | # Check if we have rights to obtain ufo and set proper expected output 93 | try: 94 | ethtool.get_ufo(loopback) 95 | expected_ufo = 'on' 96 | except (OSError, IOError): 97 | expected_ufo = 'not supported' 98 | 99 | self.assertEqual(self._output(), 100 | '''scatter-gather: on 101 | tcp segmentation offload: on 102 | udp fragmentation offload: {expected_ufo} 103 | generic segmentation offload: on 104 | generic receive offload: on 105 | '''.format(expected_ufo=expected_ufo) 106 | ) 107 | 108 | # Tests for another device 109 | if device: 110 | def test_driver_eth(self): 111 | self.assertIsNone(peth.show_driver(device)) 112 | expected_lines_start = ['driver: ', 'bus-info: '] 113 | lines = self._output().split('\n') 114 | for expected_start, line in zip(expected_lines_start, lines): 115 | self.assertTrue(line.startswith(expected_start)) 116 | 117 | def test_show_ring_eth(self): 118 | self.assertIsNone(peth.show_ring(device)) 119 | expected_lines_start = ['Ring parameters for ', 120 | 'Pre-set maximums:', 'RX:', 'RX Mini:', 121 | 'RX Jumbo:', 'TX', 'Current hardware settings:', 122 | 'RX:', 'RX Mini:', 'RX Jumbo:', 'TX:'] 123 | lines = self._output().split('\n') 124 | for expected_start, line in zip(expected_lines_start, lines): 125 | self.assertTrue(line.startswith(expected_start)) 126 | 127 | @unittest.skipIf('TRAVIS' in os.environ and os.environ['TRAVIS'] == 'true', 128 | 'Skipping this test on Travis CI because show ' 129 | 'coalesce is not supported on ethernet device in VM.') 130 | def test_show_coalesce_eth(self): 131 | self.assertIsNone(peth.show_coalesce(device)) 132 | expected_lines_start = ['Coalesce parameters for', 133 | 'Adaptive RX:', 134 | 'stats-block-usecs:', 135 | 'sample-interval:', 136 | 'pkt-rate-low:', 137 | 'pkt-rate-high:', 138 | 'rx-usecs:', 139 | 'rx-frames:', 140 | 'rx-usecs-irq:', 141 | 'rx-frames-irq:', 142 | 'tx-usecs:', 143 | 'tx-frames:', 144 | 'tx-usecs-irq:', 145 | 'tx-frames-irq:', 146 | 'rx-usecs-low:', 147 | 'rx-frame-low:', 148 | 'tx-usecs-low:', 149 | 'tx-frame-low:', 150 | 'rx-usecs-high:', 151 | 'rx-frame-high:', 152 | 'tx-usecs-high:', 153 | 'tx-frame-high:'] 154 | 155 | lines = self._output().split('\n') 156 | for expected_start, line in zip(expected_lines_start, lines): 157 | self.assertTrue(line.startswith(expected_start)) 158 | 159 | def test_show_offload_eth(self): 160 | self.assertIsNone(peth.show_offload(device)) 161 | expected_lines_start = ['scatter-gather:', 162 | 'tcp segmentation offload:', 163 | 'udp fragmentation offload:', 164 | 'generic segmentation offload:'] 165 | lines = self._output().split('\n') 166 | for expected_start, line in zip(expected_lines_start, lines): 167 | self.assertTrue(line.startswith(expected_start)) 168 | 169 | 170 | if __name__ == '__main__': 171 | unittest.main() 172 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py27,py35,py36,py37,py38,py39,py310 3 | [testenv] 4 | commands= 5 | python tests/parse_ifconfig.py -v 6 | python -m unittest discover -v 7 | passenv= 8 | TRAVIS 9 | # Run all the above commands, don't worry it reports failure anyway 10 | ignore_errors = True 11 | --------------------------------------------------------------------------------