├── .clang-format ├── .gitignore ├── .travis.yml ├── AUTHORS ├── CONTRIBUTING.md ├── COPYING ├── ChangeLog.md ├── Makefile.am ├── README.develop ├── README.md ├── TODO ├── autogen.sh ├── compat.h ├── conf.c ├── configure.ac ├── debian ├── .gitignore ├── changelog ├── compat ├── control ├── copyright ├── mini-snmpd.default ├── mini-snmpd.init ├── rules └── source │ └── format ├── ethtool-conf.h ├── freebsd.c ├── globals.c ├── java ├── .cvsignore ├── Makefile ├── README ├── UdpToTcp.8 └── UdpToTcp.java ├── lib ├── pidfile.c └── utimensat.c ├── linux.c ├── linux_ethtool.c ├── mib.c ├── mini-snmpd.8 ├── mini-snmpd.c ├── mini-snmpd.conf ├── mini-snmpd.h ├── mini-snmpd.service.in ├── protocol.c └── utils.c /.clang-format: -------------------------------------------------------------------------------- 1 | # Hey Emacs, this is -*-yaml-*- 2 | BasedOnStyle: LLVM 3 | IndentWidth: 8 4 | ColumnLimit: 0 5 | DerivePointerAlignment: false 6 | PointerAlignment: Right 7 | ReflowComments: true 8 | SpacesBeforeTrailingComments: 4 9 | AlignTrailingComments: true 10 | AlignAfterOpenBracket: Align 11 | AlignConsecutiveAssignments: true 12 | AlignConsecutiveDeclarations: true 13 | AlignEscapedNewlines: true 14 | AlignOperands: true 15 | UseTab: ForIndentation 16 | BreakBeforeBraces: Linux 17 | Cpp11BracedListStyle: false 18 | AllowShortIfStatementsOnASingleLine: false 19 | IndentCaseLabels: false 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.map 2 | *.o 3 | *~ 4 | .dirstamp 5 | .deps/* 6 | lib/.deps/* 7 | aux/* 8 | Makefile 9 | Makefile.in 10 | aclocal.m4 11 | autom4te.cache/* 12 | compile 13 | config.h 14 | config.h.in 15 | config.log 16 | config.status 17 | configure 18 | depcomp 19 | install-sh 20 | mini-snmpd 21 | mini-snmpd.service 22 | missing 23 | stamp-h1 24 | tags 25 | GPATH 26 | GRTAGS 27 | GSYMS 28 | GTAGS 29 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Travis CI integration 2 | # Defaults to GNU GCC and autotools: ./configure && make && make test 3 | language: c 4 | 5 | # Use docker for quicker builds, it now allows https://docs.travis-ci.com/user/apt/ 6 | sudo: false 7 | 8 | # Test build with both GCC and Clang (LLVM) 9 | compiler: 10 | - gcc 11 | - clang 12 | 13 | # The secure: declaration is the encrypted COVERITY_SCAN_TOKEN, created 14 | # using the "travis encrypt" command using the project repo's public key 15 | env: 16 | global: 17 | - secure: "H/4mO/azvVd+MMFWRC8h6RYFbqCdbLvM8EUNvNakjy8ozJqlk8Mi/wLOPT7pfhuiEJcNoo3s7vs/5jVy4TC5+MQRTtAJQQlXbddT5h+UBw5TRV6XNbKXXGPAZzfABKr2UUj/1sKdVw8r0sqWgnoAlu01h1Q66DsZBxX5n7XrS33riNZSgzRVJNED1OaXFQQ5PjdPvuqGahp6kZC4x5g/9GkgtAO0YXKyg6k9OQwS2Xzr01t/9btDO9A0br2a1nr1cmn3UL3n1EV8sC6ptXU2ZpEHOIuVw2wZo5PV4Es8YLFutjU7qIbcusxFvthcO6wRWchrzz1ss9eggcdgG5gq0S0xITxMbY5n1vAxRbe4qE0wAx3MgQLDlFcRlFY1Bzk1nQTjsM2iwEN8KSaRcn8calkBVlFLSnNivbfCzPBpDwB+MvUe6q0afSOK4EDR2JOzp7LfEc0SdqcbqIMiJCEGprBzubxNkmIwHF+fTrKK32SnzaN4rv7BplcbrJiRMcw7QbK8BJlS8Xi0MjOrH08CbnlVj4UCfvTT/wXPvm3vrgh5OLLNuGNSjxcfI40hUl/KIaTlvjTgIX6YPaev71Xml2yuo4fGtOOc84xWfDbe9dVnHwItvOLVVCVokMLvUV257PYKZBU23SOv+CrNdwckui6vKujCg60Bnz/lYHxXc7E=" 18 | 19 | before_install: 20 | - echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca- 21 | 22 | addons: 23 | apt: 24 | packages: 25 | - tree 26 | - libconfuse-dev 27 | coverity_scan: 28 | project: 29 | name: "troglobit/mini-snmpd" 30 | description: "A minimal SNMP agent implementation" 31 | notification_email: troglobit@gmail.com 32 | build_command_prepend: "./autogen.sh && ./configure --prefix=/" 33 | build_command: "make clean all" 34 | branch_pattern: dev 35 | 36 | # Override default build command, we do not have `make test` yet 37 | script: 38 | - ./autogen.sh 39 | - ./configure --prefix=/ 40 | - make clean 41 | - make -j5 all 42 | - DESTDIR=/tmp/ make install-strip 43 | - tree /tmp 44 | - make distcheck 45 | 46 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Joachim Nilsson 2 | Contributions: New maintainer 3 | Web: http://troglobit.com/mini-snmpd.html 4 | GitHub: https://github.com/troglobit/mini-snmpd 5 | 6 | Robert Ernst 7 | Contributios: Main branch of mini_snmpd 8 | Mail: robert.ernst@aon.at 9 | Web: http://members.aon.at/linuxfreak/linux/ 10 | 11 | Tofig Suleymanov 12 | Contributions: Patches for FreeBSD 13 | Web: http://freebsd.az/blog/mini_snmpd-freebsd-port/ 14 | 15 | Linus Lüssing 16 | Contributions: Patches for IPv6 support 17 | 18 | Javier Palacios 19 | Contributions: Lots of cleanup and code simplification 20 | 21 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing to mini-snmpd 2 | ========================== 3 | 4 | Thank you for considering contributing back to [Free Software][1]! 5 | 6 | There are a few things we would like you to consider when filing out an 7 | issue or pull request with this project: 8 | 9 | Bug Reports 10 | ----------- 11 | 12 | If you are filing out a bug report or feature request: 13 | 14 | * Please take the time to check if an issue already has been filed 15 | matching your problem. 16 | 17 | * What version are you running, have you tried the latest release? 18 | 19 | UNIX/Linux distributions often package and test software for their 20 | particular brand. If you are using a pre-packaged version, then 21 | please file a bug with that distribution first. They may use a very 22 | old version, or have multiple patches that we (upstream) don't have. 23 | 24 | 25 | Pull Requests 26 | ------------- 27 | 28 | If you have pull request or patch for a feature or bug fix. Here are a 29 | few things we want you to consider: 30 | 31 | > **Tip:** Always submit code that follows the style of surrounding code! 32 | 33 | * Coding Style 34 | 35 | The coding style itself is strictly Linux [KNF][], like GIT it is 36 | becoming a de facto standard for C programming: 37 | https://www.kernel.org/doc/Documentation/CodingStyle 38 | 39 | Lines are however allowed to be longer than 72 characters these days, 40 | there is no enforced max. length. 41 | 42 | * Logical Change Sets (**important**) 43 | 44 | Changes should be broken down into logical units that add a feature or 45 | fix a bug. Keep changes separate from each other and do not mix a bug 46 | fix with a whitespace cleanup or a new feature addition. 47 | 48 | This is important not only for readilibity, or for the possibility of 49 | maintainers to revert changes, but does also increase your chances of 50 | having a change accepted. 51 | 52 | * Commit messages 53 | 54 | Commit messages exist to track *why* a change was made. Try to be as 55 | clear and concise as possible in your commit messages, and always, be 56 | proud of your work and set up a proper GIT identity for your commits: 57 | 58 | git config --global user.name "Jane Doe" 59 | git config --global user.email jane.doe@example.com 60 | 61 | See this helpful guide for how to write simple, readable commit 62 | messages, or have at least a look at the below example. 63 | 64 | http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html 65 | 66 | 67 | Example Commit 68 | -------------- 69 | 70 | Example commit message from the [Pro Git][gitbook] online book, notice 71 | how `git commit -s` is used to automatically add a `Signed-off-by`: 72 | 73 | Capitalized, short (50 chars or less) summary 74 | 75 | More detailed explanatory text, if necessary. Wrap it to about 72 76 | characters or so. In some contexts, the first line is treated as the 77 | subject of an email and the rest of the text as the body. The blank 78 | line separating the summary from the body is critical (unless you omit 79 | the body entirely); tools like rebase can get confused if you run the 80 | two together. 81 | 82 | Write your commit message in the imperative: "Fix bug" and not "Fixed bug" 83 | or "Fixes bug." This convention matches up with commit messages generated 84 | by commands like git merge and git revert. 85 | 86 | Further paragraphs come after blank lines. 87 | 88 | - Bullet points are okay, too 89 | 90 | - Typically a hyphen or asterisk is used for the bullet, followed by a 91 | single space, with blank lines in between, but conventions vary here 92 | 93 | - Use a hanging indent 94 | 95 | Signed-off-by: Some Namesson 96 | 97 | 98 | [1]: http://www.gnu.org/philosophy/free-sw.en.html 99 | [KNF]: https://en.wikipedia.org/wiki/Kernel_Normal_Form 100 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 5 | 51 Franklin St, 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 Library 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 St, Fifth Floor, Boston, MA 02110-1301 USA 309 | 310 | 311 | Also add information on how to contact you by electronic and paper mail. 312 | 313 | If the program is interactive, make it output a short notice like this 314 | when it starts in an interactive mode: 315 | 316 | Gnomovision version 69, Copyright (C) year name of author 317 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 318 | This is free software, and you are welcome to redistribute it 319 | under certain conditions; type `show c' for details. 320 | 321 | The hypothetical commands `show w' and `show c' should show the appropriate 322 | parts of the General Public License. Of course, the commands you use may 323 | be called something other than `show w' and `show c'; they could even be 324 | mouse-clicks or menu items--whatever suits your program. 325 | 326 | You should also get your employer (if you work as a programmer) or your 327 | school, if any, to sign a "copyright disclaimer" for the program, if 328 | necessary. Here is a sample; alter the names: 329 | 330 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 331 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 332 | 333 | , 1 April 1989 334 | Ty Coon, President of Vice 335 | 336 | This General Public License does not permit incorporating your program into 337 | proprietary programs. If your program is a subroutine library, you may 338 | consider it more useful to permit linking proprietary applications with the 339 | library. If this is what you want to do, use the GNU Library General 340 | Public License instead of this License. 341 | -------------------------------------------------------------------------------- /ChangeLog.md: -------------------------------------------------------------------------------- 1 | Change Log 2 | ========== 3 | 4 | All notable changes to the project are documented in this file. 5 | 6 | 7 | [v1.6][] -- 2020-02-23 8 | ---------------------- 9 | 10 | Bug fix release. 11 | 12 | ### Changes 13 | - Added debian/ packaging, available from https://deb.troglobit.com 14 | 15 | ### Fixes 16 | - Fix #16: regression in ifTable for point-to-point interfaces 17 | - Fix #17: major memory leak in Linux backend 18 | - Fix #18: consistent timeout handling in .conf file and command line 19 | 20 | 21 | [v1.5][] -- 2020-02-03 22 | ---------------------- 23 | 24 | Major feature release. Support for TCP-MIB, UDP-MIB, IP-MIB, 25 | ifXTable with 64-bit counters. 26 | 27 | - Majority of new features from [NDM Systems][] 28 | - CVE fixes from [Cisco Talos Intelligence Group][talos] 29 | 30 | ### Changes 31 | - Add support for ifXTable (64-bit counters), from NDM Systems 32 | - Add support for TCP-MIB, from NDM Systems 33 | - Add support for UDP-MIB, from NDM Systems 34 | - Add support for IP-MIB, from NDM Systems 35 | - Add support for ifType 36 | - Add support for ifMtu 37 | - Binary and man page renamed: `mini_snmpd` --> `mini-snmpd` 38 | - New command line option `-l LEVEL` replaces `--verbose` 39 | - New command line option `-v` to show program version 40 | - Create PID file when daemon is ready to receive signals 41 | - Add support for systemd unit file on Linux 42 | - Add support for /etc/mini-snmpd.conf, disabled by default 43 | 44 | ### Fixes 45 | - CVE-2020-6060: Fix stack overflow in client connection handler 46 | - CVE-2020-6059: Fix out-of-bounds read in parsing of SNMP packet 47 | - CVE-2020-6058: Fix out-of-bounds read in parsing of SNMP packet 48 | - Let `-s` flag control use of syslog, when running in foreground 49 | - Removed all (known) GNU:isms; i.e., `__progname` and `%m` 50 | 51 | 52 | [v1.4][] -- 2017-06-26 53 | ---------------------- 54 | 55 | Bug fix release, courtesy of Andre Grosse Bley, @Haeretiker. 56 | 57 | ### Changes 58 | - Increase MIB table size: 128 --> 192 59 | 60 | ### Fixes 61 | - Fix default install directory: `$prefix/bin` --> `$prefix/sbin` 62 | - Fix inverted enable/disable options to `configure` script, issue #4 63 | - Incorrect OID types: `ifLastChange` should be `BER_TYPE_TIME_TICKS` 64 | and `ifSpeed` should be `BER_TYPE_GAUGE`, issue #1 65 | - Fix `parse_line()` to prevent partial matches: `wlan0` matched both 66 | `wlan0-1` and `wlan0-2`, issue #1 67 | - Fix `parse_lineint()` to prevent partial matches, issue #1 68 | - Response OID order match with request order, reversed order breaks at 69 | least the MRTG SNMP client, issue #1 70 | - Traffic counters get stuck after 4GB traffic. Use `strtoull()` rather 71 | than `strtoul()` to parse numbers, issue #1 72 | - OIDs in request can be in any order. Reset OID table position after 73 | each handled OID from request, issue #1 74 | 75 | 76 | [v1.3][] -- 2015-11-23 77 | ---------------------- 78 | 79 | ### Changes 80 | 81 | - Refactor and cleanup by [Javier Palacios][palacios] 82 | - New maintainer, [Joachim Nilsson][troglobit] 83 | - Hosting is now on [GitHub][home] 84 | - Changed to GNU Configure and Build System, use `./autogen.sh` for 85 | first time checkout from GIT 86 | - Reduced stack usage in Linux `/proc` file parser backend 87 | - Add support for daemonizing automatically, `-n` for previous behavior 88 | - Add support for logging to syslog even when running in the foreground 89 | - Complete refactor of FreeBSD support. Now with native syscalls instead 90 | of requirment for Linux `/proc` file system 91 | - Add support for daemonizing by default, use `-n` to run in foreground 92 | - Add support for syslog even if running in the foreground 93 | - Dual stack support, IPv4 default, when building with `--enable-ipv6`, 94 | which is also default 95 | - Use `sigaction()` instead of `signal()` and `siginterrupt()`, by 96 | [Henrik Nordstrom][hno] 97 | - Increase MAX number of interfaces to monitor from four to eight, by 98 | [Henrik Nordstrom][hno] 99 | 100 | ### Fixes 101 | - From [Vladimir N. Oleynik][dzo]'s [Busybox fork][vodz-fork]: 102 | - Do not allow ':' as interface separator 103 | - Simplify `read_values()` and its callee's, skip optional ':' 104 | - Inspirations for lots of reduced stack usage 105 | - Fix typo in `setsockopt()` 106 | - Massive code cleanup and simplification by [Joachim Nilsson][troglobit] 107 | - FreeBSD build fixes, e.g. `SO_BINDDEVICE` socket option does not exist 108 | - Display OK log message *after* successful socket & bind 109 | 110 | 111 | [v1.2b][] -- 2010-03-28 112 | ----------------------- 113 | 114 | ### Changes 115 | 116 | - Added support for compilation for IPv4-only kernels 117 | 118 | ### Fixes 119 | 120 | - Fixed bug in encoding of integers with 24 significant bits 121 | 122 | 123 | [v1.1][] -- 2010-02-25 124 | ---------------------- 125 | 126 | ### Changes 127 | 128 | - Added support for IPv6 129 | 130 | 131 | v1.0 -- 2009-01-27 132 | ------------------ 133 | 134 | ### Fixes 135 | 136 | - Fixed calculation of ticks since last MIB update (integer calculation 137 | resulted in overflows, updates not done in cases of error or time 138 | running backwards) 139 | 140 | 141 | v0.8 -- 2008-10-08 142 | ------------------ 143 | 144 | ### Fixes 145 | 146 | - Fixed calculation of free inodes in percent for filesystems that do 147 | not support getting the number of inodes (for example FAT) 148 | 149 | 150 | v0.7 -- 2008-10-06 151 | ------------------ 152 | 153 | ### Fixes 154 | 155 | - Fixed `get_process_uptime()` function to work regardless of time 156 | changes 157 | 158 | 159 | v0.6 -- 2008-10-04 160 | ------------------ 161 | 162 | ### Changes 163 | 164 | - Split `utils.c` into common and operating-system specific functions 165 | - Added an install target 166 | - Added some developer documentation 167 | - Added patches for FreeBSD support 168 | 169 | ### Fixes 170 | 171 | - Reduced memory consumption: the `get*info()` functions now use a 172 | buffer provided by the caller rather than their static buffers 173 | 174 | 175 | v0.5 -- 2008-09-29 176 | ------------------ 177 | 178 | ### Changes 179 | 180 | - Added CHANGELOG and TODO file 181 | - Added a check for the file descriptors of TCP connections 182 | 183 | v0.4 -- 2008-09-28 184 | ------------------ 185 | 186 | This is the first feature-complete version. SNMP get, getnext, and 187 | getbulk are supported on UDP and TCP connections. 188 | 189 | 190 | [UNRELEASED]: https://github.com/troglobit/mini-snmpd/compare/v1.6...HEAD 191 | [v1.6]: https://github.com/troglobit/mini-snmpd/compare/v1.5...v1.6 192 | [v1.5]: https://github.com/troglobit/mini-snmpd/compare/v1.4...v1.5 193 | [v1.4]: https://github.com/troglobit/mini-snmpd/compare/v1.3...v1.4 194 | [v1.3]: https://github.com/troglobit/mini-snmpd/compare/v1.2b...v1.3 195 | [v1.2b]: https://github.com/troglobit/mini-snmpd/compare/v1.1...v1.2b 196 | [v1.1]: https://github.com/troglobit/mini-snmpd/compare/v1.0...v1.1 197 | [dzo]: 198 | [hno]: https://github.com/hno 199 | [home]: https://github.com/troglobit/mini-snmpd 200 | [palacios]: https://github.com/javiplx 201 | [NDM Systems]: https://github.com/ndmsystems 202 | [talos]: https://twitter.com/talossecurity 203 | [troglobit]: https://github.com/troglobit 204 | [vodz-fork]: http://www.simtreas.ru/~dzo/busybox-vodz.html 205 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | EXEC = mini-snmpd 2 | EXTRA_DIST = README.md COPYING ChangeLog.md 3 | doc_DATA = README.md COPYING 4 | dist_man8_MANS = $(EXEC).8 5 | sbin_PROGRAMS = $(EXEC) 6 | AM_CPPFLAGS = -DSYSCONFDIR=\"@sysconfdir@\" -DRUNSTATEDIR=\"@runstatedir@\" 7 | 8 | mini_snmpd_SOURCES = mini-snmpd.c mini-snmpd.h linux.c freebsd.c mib.c \ 9 | globals.c protocol.c utils.c compat.h 10 | if HAVE_CONFUSE 11 | mini_snmpd_SOURCES += conf.c linux_ethtool.c 12 | endif 13 | mini_snmpd_CPPFLAGS = $(AM_CPPFLAGS) 14 | mini_snmpd_CFLAGS = -W -Wall -Wextra -std=gnu99 15 | mini_snmpd_LDADD = $(LIBS) $(LIBOBJS) 16 | mini_snmpd_CFLAGS += $(confuse_CFLAGS) 17 | mini_snmpd_LDADD += $(confuse_LIBS) 18 | 19 | if HAVE_CONFUSE 20 | dist_sysconf_DATA = mini-snmpd.conf 21 | endif 22 | 23 | if HAVE_SYSTEMD 24 | systemd_DATA = mini-snmpd.service 25 | endif 26 | 27 | ## Generate MD5 checksum file 28 | MD5 = md5sum 29 | md5-dist: 30 | @for file in $(DIST_ARCHIVES); do \ 31 | $(MD5) $$file > ../$$file.md5; \ 32 | done 33 | 34 | ## Check if tagged in git 35 | release-hook: 36 | @if [ ! `git tag | grep $(PACKAGE_VERSION)` ]; then \ 37 | echo; \ 38 | printf "\e[1m\e[41mCannot find release tag $(PACKAGE_VERSION)\e[0m\n"; \ 39 | printf "\e[1m\e[5mDo release anyway?\e[0m "; read yorn; \ 40 | if [ "$$yorn" != "y" -a "$$yorn" != "Y" ]; then \ 41 | printf "OK, aborting release.\n"; \ 42 | exit 1; \ 43 | fi; \ 44 | echo; \ 45 | else \ 46 | echo; \ 47 | printf "\e[1m\e[42mFound GIT release tag $(PACKAGE_VERSION)\e[0m\n"; \ 48 | printf "\e[1m\e[44m>>Remember to push tags!\e[0m\n"; \ 49 | echo; \ 50 | fi 51 | 52 | # lintian --profile debian -i -I --show-overrides ../$PKG.changes 53 | package build-deb: 54 | @dpkg-buildpackage -uc -us -B 55 | 56 | ## Target to run when building a release 57 | release: release-hook distcheck package md5-dist 58 | @mv $(DIST_ARCHIVES) ../ 59 | @echo 60 | @echo "Resulting release files:" 61 | @echo "=================================================================" 62 | @for file in $(DIST_ARCHIVES); do \ 63 | printf "%-32s Distribution tarball\n" $$file; \ 64 | printf "%-32s " $$file.md5; cat ../$$file.md5 | cut -f1 -d' '; \ 65 | done 66 | @for file in `cd ..; ls $(PACKAGE)_$(VERSION)*`; do \ 67 | printf "%-32s Debian/Ubuntu package\n" $$file; \ 68 | done 69 | 70 | # Workaround for systemd unit file duing distcheck 71 | DISTCHECK_CONFIGURE_FLAGS = --with-systemd=$$dc_install_base/$(systemd) 72 | DISTCLEANFILES = lib/.libs/* *~ *.bak *.map .*.d *.d DEADJOE semantic.cache *.gdb *.elf core core.* 73 | -------------------------------------------------------------------------------- /README.develop: -------------------------------------------------------------------------------- 1 | The Mini SNMP daemon does not support passing through SNMP requests to other 2 | processes or loading plugins. Thus, if you want to extend mini-snmpd by your 3 | own MIB variables, you need to do the following: 4 | 5 | 6 | 7 | 1.) Obtain a vendor OID 8 | 9 | The default vendor OID is only a placeholder where you need to insert the PEN 10 | (private enterprise number) of your organization. If you do not have one, you 11 | can get one assigned by the IANA (internet assigned numbers authority) free of 12 | charge. See their PEN application webpage at http://pen.iana.org/. 13 | 14 | The vendor is set in the Makefile (the "VENDOR=" make variable). 15 | 16 | 17 | 18 | 2.) Write the MIB file 19 | 20 | This is not needed for mini-snmpd, but you should be prepared to deliver the 21 | MIB description in text form to your users for convenience. Examples of MIBs 22 | can be found in the net-snmp package. 23 | 24 | 25 | 26 | 3.) Add a define and the code for your extension 27 | 28 | You should make your extension a compile-time option and put all specific code 29 | in #ifdef/#endif blocks to make it easier to distinguish between built-in and 30 | added code. 31 | 32 | The "demo" extension example, which provides two random integers, shows how to 33 | do this (CONFIG_ENABLE_DEMO in the sources). Note that it uses the PEN 99999, 34 | which is not yet assigned by the IANA, but since there is no warranty that this 35 | doesn't happen, do not enable the demo extension in a release version! 36 | 37 | 38 | 39 | 4.) Things to consider 40 | 41 | The MIB table has a fixed maximum length (set by the MAX_NR_VALUES define). If 42 | you get a runtime error about a table overflow when creating the MIB entry, you 43 | need to increase that value. 44 | 45 | The mib_update() function is called on every received request, to give the 46 | handlers the possibility to decide whether to update their corresponding MIB 47 | variables everytime (for example the system uptime) or only in the interval 48 | specified by the -t commandline parameter (for example the disk info). To save 49 | CPU cycles, time-consuming updates or updates of values that change only slowly 50 | should made dependent on the timeout counter. 51 | 52 | For variables of type "octet string", you need to call mib_build_entry() with 53 | a string with the maximum length that your variable can have during runtime. 54 | This is needed by mib_build_entry() to determine the size of the data buffer 55 | for that MIB entry. 56 | 57 | If the function you use to determine the new MIB values is operating system 58 | dependent, you should add your code to both linux.c and/or freebsd.c instead 59 | of utils.c (which should only be used for os-independent functions). 60 | 61 | For debugging output, use the logit() macro instead of hardcoding printf() or 62 | syslog() calls. 63 | 64 | 65 | 66 | Robert Ernst 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Mini SNMP Daemon 2 | ================ 3 | [![License Badge][]][License] [![Travis Status][]][Travis] [![Coverity Status][]][Coverity Scan] 4 | 5 | > The [latest release][releases] is always available from GitHub. 6 | > Download only versioned tarballs, `mini-snmpd-X.Y.tar.gz`. See 7 | > below for instructions on how to build. 8 | 9 | Table of Contents 10 | ----------------- 11 | 12 | * [Introduction](#introduction) 13 | * [Examples](#examples) 14 | * [Build & Install](#build--install) 15 | * [Building from GIT](#building-from-git) 16 | * [Origin & References](#origin--references) 17 | 18 | 19 | Introduction 20 | ------------ 21 | 22 | The Mini SNMP daemon is a minimal implementation of an SNMP daemon. It 23 | is primarily targeted at embedded systems with limited disk and memory 24 | resources. All configuration can be done using command line arguments, 25 | or using the optional `/etc/mini-snmp.conf` file. It supports basic 26 | CPU, memory, disk, and network interface statistics. 27 | 28 | `mini-snmpd` is not as flexibible as, and does not support the same 29 | features as, the de-facto standard [net-snmp][], but this also means 30 | it does not have the same footprint and overhead. 31 | 32 | Supported features: 33 | 34 | * SNMP version 1 and 2c 35 | * Community string authentication when using 2c or explicitly configured 36 | * Read-only access (writing is not supported) 37 | * Includes basic system info like CPU load, memory, disk and network interfaces 38 | * Does not need a configuration file, but one is supported 39 | * Supports UDP and TCP (thus supports SSH tunneling of SNMP connections) 40 | * Supports Linux kernel versions 2.4, 2.6, and later 41 | * Supports FreeBSD (needs procfs mounted using "mount_linprocfs procfs /proc") 42 | 43 | `mini-snmpd` has been tested on x86 and ARM platforms in Ubuntu Linux, 44 | Alpine Linux, and FreeBSD, using net-snmp and SnmpB as clients. Big 45 | endian may not work. 46 | 47 | - For info about licensing, see the file [COPYING][license] 48 | - For info about using the program, see the file [mini-snmpd.8][man] 49 | - For info about how to (cross)compile the program, see the file [Makefile][build] 50 | - For info about how to extend the MIB, see the file [README.develop][contrib] 51 | 52 | 53 | Examples 54 | -------- 55 | 56 | Start the daemon: 57 | 58 | ./mini-snmpd -n -p 16161 -D "My laptop" -L "Batcave" \ 59 | -C "Ops " -d '/' -i wlp3s0 60 | 61 | Check uptime, useful to "ping" a device over SNMP: 62 | 63 | snmpget -c public -v2c 127.0.0.1:16161 system.sysUpTime.0 64 | DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (93103) 0:15:31.03 65 | 66 | Complete walk: 67 | 68 | snmpwalk -v2c -c public 127.0.0.1:16161 69 | SNMPv2-MIB::sysDescr.0 = STRING: My laptop 70 | SNMPv2-MIB::sysObjectID.0 = OID: SNMPv2-SMI::enterprises 71 | DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (93103) 0:15:31.03 72 | SNMPv2-MIB::sysContact.0 = STRING: Ops 73 | SNMPv2-MIB::sysName.0 = STRING: luthien 74 | SNMPv2-MIB::sysLocation.0 = STRING: Batcave 75 | IF-MIB::ifNumber.0 = INTEGER: 1 76 | IF-MIB::ifIndex.1 = INTEGER: 1 77 | IF-MIB::ifDescr.1 = STRING: wlp3s0 78 | IF-MIB::ifType.1 = INTEGER: ethernetCsmacd(6) 79 | IF-MIB::ifMtu.1 = INTEGER: 1500 80 | IF-MIB::ifSpeed.1 = Gauge32: 1000000000 81 | IF-MIB::ifPhysAddress.1 = STRING: 6c:88:14:48:57:1c 82 | IF-MIB::ifAdminStatus.1 = INTEGER: up(1) 83 | IF-MIB::ifOperStatus.1 = INTEGER: up(1) 84 | IF-MIB::ifLastChange.1 = Timeticks: (0) 0:00:00.00 85 | IF-MIB::ifInOctets.1 = Counter32: 207845364 86 | IF-MIB::ifInUcastPkts.1 = Counter32: 154221 87 | IF-MIB::ifInDiscards.1 = Counter32: 0 88 | IF-MIB::ifInErrors.1 = Counter32: 0 89 | IF-MIB::ifOutOctets.1 = Counter32: 13323787 90 | IF-MIB::ifOutUcastPkts.1 = Counter32: 88071 91 | IF-MIB::ifOutDiscards.1 = Counter32: 0 92 | IF-MIB::ifOutErrors.1 = Counter32: 0 93 | HOST-RESOURCES-MIB::hrSystemUptime.0 = Timeticks: (454155) 1:15:41.55 94 | 95 | Check load average: 96 | 97 | snmpwalk -v2c -c public 127.0.0.1:16161 UCD-SNMP-MIB::laLoad 98 | UCD-SNMP-MIB::laLoad.1 = STRING: 0.56 99 | UCD-SNMP-MIB::laLoad.2 = STRING: 0.46 100 | UCD-SNMP-MIB::laLoad.3 = STRING: 0.36 101 | 102 | Check monitored disks: 103 | 104 | snmpwalk -v2c -c public 127.0.0.1:16161 UCD-SNMP-MIB::dskTable 105 | UCD-SNMP-MIB::dskIndex.1 = INTEGER: 1 106 | UCD-SNMP-MIB::dskPath.1 = STRING: / 107 | UCD-SNMP-MIB::dskTotal.1 = INTEGER: 245084448 108 | UCD-SNMP-MIB::dskAvail.1 = INTEGER: 38953552 109 | UCD-SNMP-MIB::dskUsed.1 = INTEGER: 206130896 110 | UCD-SNMP-MIB::dskPercent.1 = INTEGER: 85 111 | UCD-SNMP-MIB::dskPercentNode.1 = INTEGER: 10 112 | 113 | 114 | Build & Install 115 | --------------- 116 | 117 | The [GNU Configure & Build][buildsystem] system use `/usr/local` as the 118 | default install prefix. Usually this is sufficient, the below example 119 | installs to `/usr` instead: 120 | 121 | tar xf mini-snmpd-X.Y.tar.xz 122 | cd mini-snmpd-X.Y/ 123 | ./configure --prefix=/usr 124 | make -j5 125 | sudo make install-strip 126 | 127 | To use the `/etc/mini-snmpd.conf` support, both the `pkgconfig` and 128 | `libConfuse` packages must be installed. Installing from pre-built 129 | packages differ between systems, check naming and suffix (`-dev`) to 130 | match your system. 131 | 132 | > **Note:** mini-snmpd-X.Y.tar.gz is not an actual release. See the 133 | > [releases page on GitHub][releases] for the latest versioned release. 134 | 135 | 136 | Building from GIT 137 | ----------------- 138 | 139 | If you want to contribute, or simply want to try out the latest but 140 | still unreleased features, then you need to know a few things about 141 | the [GNU Configure & Build][buildsystem] system: 142 | 143 | - `configure.ac` and a per-directory `Makefile.am` are key files 144 | - `configure` and `Makefile.in` are generated from `autogen.sh`, 145 | they are not stored in GIT but automatically generated for the 146 | release tarballs 147 | - `Makefile` is generated by `configure` script 148 | 149 | To build from GIT you first need to clone the repository and run the 150 | `autogen.sh` script. This requires `automake` and `autoconf` to be 151 | installed on your system. 152 | 153 | git clone https://github.com/troglobit/mini-snmpd.git 154 | cd mini-snmpd/ 155 | ./autogen.sh 156 | ./configure && make 157 | 158 | GIT sources are a moving target and are not recommended for production 159 | systems, unless you know what you are doing! 160 | 161 | 162 | Origin & References 163 | ------------------- 164 | 165 | [mini-snmpd][github] is an effort by [Joachim Nilsson][] to create a 166 | focal point for patches and development of the original [mini_snmpd][1] 167 | project by [Robert Ernst][], since the original site now has gone dark. 168 | 169 | The new project is [maintained at GitHub][github]. Use its issue tracker 170 | and pull request functions to report bugs or contribute new features. 171 | 172 | [1]: http://members.aon.at/linuxfreak/linux/mini_snmpd.html 173 | [man]: https://man.troglobit.com/man8/mini-snmpd.8.html 174 | [github]: https://github.com/troglobit/mini-snmpd 175 | [license]: https://github.com/troglobit/mini-snmpd/blob/master/COPYING 176 | [contrib]: https://github.com/troglobit/mini-snmpd/blob/master/README.develop 177 | [build]: https://github.com/troglobit/mini-snmpd/blob/master/Makefile 178 | [Joachim Nilsson]: https://troglobit.com 179 | [Robert Ernst]: 180 | [net-snmp]: https://www.net-snmp.org/ 181 | [buildsystem]: https://airs.com/ian/configure/ 182 | [releases]: https://github.com/troglobit/mini-snmpd/releases 183 | [License]: https://en.wikipedia.org/wiki/GPL_license 184 | [License Badge]: https://img.shields.io/badge/License-GPL%20v2-blue.svg 185 | [Travis]: https://travis-ci.org/troglobit/mini-snmpd 186 | [Travis Status]: https://travis-ci.org/troglobit/mini-snmpd.png?branch=master 187 | [Coverity Scan]: https://scan.coverity.com/projects/15696 188 | [Coverity Status]: https://scan.coverity.com/projects/15696/badge.svg 189 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | * Merge length calculation and encoding in mib.c and protocol.c 2 | 3 | Some length calculation and encoding code exists in both mib.c and 4 | protocol.c with only slight differences; try to merge that 5 | 6 | * Audit encoding functions in mib.c 7 | 8 | The encode functions in mib.c do not check the size; this can be a 9 | problem when forgetting to initialize octet string MIB variables with 10 | their maximum length (only for strings, not for integers) 11 | 12 | * Check system MIB against RFC for missing required OIDs 13 | 14 | Check the relevant RFCs for the SNMP V1 and V2 MIBs and which parts of 15 | the system MIB are must be included 16 | 17 | * Audit FreeBSD backend, missing functionality still 18 | 19 | The FreeBSD backend has received native support in v1.3 so it does not 20 | need to use Linux /proc compat anymore. However, some parts are not 21 | fully implemented yet. 22 | 23 | * Add SNMPv3 support, RFC2574/RFC3414 24 | 25 | At least user-based security. 26 | 27 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | autoreconf -W portability -visfm 4 | -------------------------------------------------------------------------------- /compat.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2008-2010 Robert Ernst 3 | * Copyright (C) 2015-2020 Joachim Nilsson 4 | * 5 | * This file may be distributed and/or modified under the terms of the 6 | * GNU General Public License version 2 as published by the Free Software 7 | * Foundation and appearing in the file LICENSE.GPL included in the 8 | * packaging of this file. 9 | * 10 | * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE 11 | * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 12 | * 13 | * See COPYING for GPL licensing information. 14 | */ 15 | 16 | #ifndef MINI_SNMPD_COMPAT_H_ 17 | #define MINI_SNMPD_COMPAT_H_ 18 | 19 | #include "config.h" 20 | #include 21 | #include 22 | 23 | #ifndef UNUSED 24 | #define UNUSED(x) x __attribute__((unused)) 25 | #endif 26 | 27 | /* From The Practice of Programming, by Kernighan and Pike */ 28 | #ifndef NELEMS 29 | #define NELEMS(array) (sizeof(array) / sizeof(array[0])) 30 | #endif 31 | 32 | #ifndef HAVE_PIDFILE 33 | int pidfile(const char *basename); 34 | #endif 35 | 36 | #ifndef HAVE_UTIMENSAT 37 | int utimensat(int dirfd, const char *pathname, const struct timespec ts[2], int flags); 38 | #endif 39 | 40 | #ifndef HAVE_GETPROGNAME 41 | static inline char *getprogname(void) 42 | { 43 | extern char *g_prognm; 44 | return g_prognm; 45 | } 46 | #endif 47 | 48 | #endif /* MINI_SNMPD_COMPAT_H_ */ 49 | -------------------------------------------------------------------------------- /conf.c: -------------------------------------------------------------------------------- 1 | /* .conf parser 2 | * 3 | * Copyright (C) 2018-2020 Joachim Nilsson 4 | * 5 | * This file may be distributed and/or modified under the terms of the 6 | * GNU General Public License version 2 as published by the Free Software 7 | * Foundation and appearing in the file LICENSE.GPL included in the 8 | * packaging of this file. 9 | * 10 | * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE 11 | * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 12 | * 13 | * See COPYING for GPL licensing information. 14 | */ 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include "ethtool-conf.h" 21 | #include "mini-snmpd.h" 22 | 23 | static cfg_t *cfg = NULL; 24 | 25 | static void conf_errfunc(cfg_t *cfg, const char *format, va_list args) 26 | { 27 | char fmt[80]; 28 | char buf[256]; 29 | 30 | if (cfg && cfg->filename && cfg->line) 31 | snprintf(fmt, sizeof(fmt), "%s:%d: %s\n", cfg->filename, cfg->line, format); 32 | else if (cfg && cfg->filename) 33 | snprintf(fmt, sizeof(fmt), "%s: %s\n", cfg->filename, format); 34 | else 35 | snprintf(fmt, sizeof(fmt), "%s\n", format); 36 | vsnprintf(buf, sizeof(buf), fmt, args); 37 | 38 | logit(LOG_ERR, 0, "%s", buf); 39 | } 40 | 41 | static char *get_string(cfg_t *cfg, const char *key) 42 | { 43 | char *str; 44 | 45 | str = cfg_getstr(cfg, key); 46 | if (str) 47 | return strdup(str); 48 | 49 | return NULL; 50 | } 51 | 52 | static size_t get_list(cfg_t *cfg, const char *key, char **list, size_t len) 53 | { 54 | size_t i = 0; 55 | 56 | while (i < cfg_size(cfg, key)) { 57 | char *str; 58 | 59 | str = cfg_getnstr(cfg, key, i); 60 | if (str && i < len) 61 | list[i++] = strdup(str); 62 | } 63 | 64 | return i; 65 | } 66 | 67 | int read_config(char *file) 68 | { 69 | int rc = 0; 70 | cfg_opt_t ethtool_opts[] = { 71 | CFG_STR("rx_bytes", NULL, CFGF_NONE), 72 | CFG_STR("rx_mc_packets", NULL, CFGF_NONE), 73 | CFG_STR("rx_bc_packets", NULL, CFGF_NONE), 74 | CFG_STR("rx_packets", NULL, CFGF_NONE), 75 | CFG_STR("rx_errors", NULL, CFGF_NONE), 76 | CFG_STR("rx_drops", NULL, CFGF_NONE), 77 | CFG_STR("tx_bytes", NULL, CFGF_NONE), 78 | CFG_STR("tx_mc_packets", NULL, CFGF_NONE), 79 | CFG_STR("tx_bc_packets", NULL, CFGF_NONE), 80 | CFG_STR("tx_packets", NULL, CFGF_NONE), 81 | CFG_STR("tx_errors", NULL, CFGF_NONE), 82 | CFG_STR("tx_drops", NULL, CFGF_NONE), 83 | CFG_END() 84 | }; 85 | cfg_opt_t opts[] = { 86 | CFG_STR ("location", NULL, CFGF_NONE), 87 | CFG_STR ("contact", NULL, CFGF_NONE), 88 | CFG_STR ("description", NULL, CFGF_NONE), 89 | CFG_BOOL("authentication", g_auth, CFGF_NONE), 90 | CFG_STR ("community", NULL, CFGF_NONE), 91 | CFG_INT ("timeout", g_timeout, CFGF_NONE), 92 | CFG_STR ("vendor", VENDOR, CFGF_NONE), 93 | CFG_STR_LIST("disk-table", "/", CFGF_NONE), 94 | CFG_STR_LIST("iface-table", NULL, CFGF_NONE), 95 | CFG_SEC("ethtool", ethtool_opts, CFGF_MULTI | CFGF_TITLE | CFGF_NO_TITLE_DUPES), 96 | CFG_END() 97 | }; 98 | 99 | if (access(file, F_OK)) 100 | return 0; 101 | 102 | cfg = cfg_init(opts, CFGF_NONE); 103 | if (!cfg) { 104 | logit(LOG_ERR, errno, "Failed initializing configuration file parser"); 105 | return 1; 106 | } 107 | 108 | /* Custom logging, rather than default Confuse stderr logging */ 109 | cfg_set_error_function(cfg, conf_errfunc); 110 | 111 | rc = cfg_parse(cfg, file); 112 | switch (rc) { 113 | case CFG_FILE_ERROR: 114 | logit(LOG_ERR, 0, "Cannot read configuration file %s", file); 115 | goto error; 116 | 117 | case CFG_PARSE_ERROR: 118 | logit(LOG_ERR, 0, "Parse error in %s", file); 119 | goto error; 120 | 121 | case CFG_SUCCESS: 122 | break; 123 | } 124 | 125 | g_location = get_string(cfg, "location"); 126 | g_contact = get_string(cfg, "contact"); 127 | g_description = get_string(cfg, "description"); 128 | 129 | g_disk_list_length = get_list(cfg, "disk-table", g_disk_list, NELEMS(g_disk_list)); 130 | g_interface_list_length = get_list(cfg, "iface-table", g_interface_list, NELEMS(g_interface_list)); 131 | 132 | g_auth = cfg_getbool(cfg, "authentication"); 133 | g_community = get_string(cfg, "community"); 134 | g_timeout = cfg_getint(cfg, "timeout"); 135 | 136 | g_vendor = get_string(cfg, "vendor"); 137 | 138 | ethtool_xlate_cfg(cfg); 139 | 140 | error: 141 | cfg_free(cfg); 142 | return rc; 143 | } 144 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_INIT([mini-snmpd], [1.6], [https://github.com/troglobit/mini-snmpd/issues],, 2 | [https://troglobit.com/projects/mini-snmpd/]) 3 | AC_CONFIG_AUX_DIR(aux) 4 | AM_INIT_AUTOMAKE([1.11 foreign subdir-objects]) 5 | AM_SILENT_RULES([yes]) 6 | 7 | AC_CONFIG_SRCDIR([mini-snmpd.c]) 8 | AC_CONFIG_HEADER([config.h]) 9 | AC_CONFIG_FILES([Makefile mini-snmpd.service]) 10 | 11 | AC_PROG_CC 12 | AC_PROG_INSTALL 13 | 14 | # Check for required packages: libconfuse, systemd 15 | PKG_PROG_PKG_CONFIG 16 | 17 | # Check for usually missing API's, which we can replace 18 | AC_REPLACE_FUNCS([pidfile utimensat]) 19 | AC_CONFIG_LIBOBJ_DIR([lib]) 20 | 21 | # Check for other library functions 22 | AC_CHECK_FUNCS([getprogname]) 23 | 24 | # Check for header files, e.g. alloca.h doesn't exist on FreeBSD 25 | AC_HEADER_STDC 26 | AC_CHECK_HEADERS(alloca.h unistd.h stdint.h stdlib.h syslog.h signal.h getopt.h arpa/inet.h) 27 | AC_CHECK_HEADERS(sys/socket.h sys/time.h time.h sys/types.h net/if.h netinet/in.h) 28 | AC_CHECK_FUNCS(strstr strtod strtoul strtok getopt) 29 | 30 | ### Check for configured features ############################################################# 31 | AC_ARG_WITH(vendor, 32 | AS_HELP_STRING([--with-vendor=OID], [Set a different vendor OID, default: .1.3.6.1.4.1]), 33 | [vendor="$withval"], [vendor=".1.3.6.1.4.1"]) 34 | 35 | AC_ARG_WITH([config], 36 | AS_HELP_STRING([--with-config], [Enable /etc/mini-snmpd.conf support using libConfuse]), 37 | [with_config=$withval], [with_config=no]) 38 | 39 | AC_ARG_WITH(systemd, 40 | [AS_HELP_STRING([--with-systemd=DIR], [Directory for systemd service files])],, 41 | [with_systemd=auto]) 42 | 43 | AC_ARG_ENABLE(debug, 44 | AS_HELP_STRING([--enable-debug], [Enable developer debug mode, disabled by default]), 45 | [enable_debug=$enableval], [enable_debug=no]) 46 | 47 | AC_ARG_ENABLE(demo, 48 | AS_HELP_STRING([--enable-demo], [Enable demo mode, devs only, disabled by default]), 49 | [enable_demo=$enableval], [enable_demo=no]) 50 | 51 | AC_ARG_ENABLE(ipv6, 52 | AS_HELP_STRING([--disable-ipv6], [Disable IPv6 support, enabled by default]), 53 | [enable_ipv6=$enableval], [enable_ipv6=yes]) 54 | 55 | AC_ARG_ENABLE(ethtool, 56 | AS_HELP_STRING([--enable-ethtool], [Enable ethtool interface stats, disabled by default]), 57 | [enable_ethtool=$enableval], [enable_ethtool=no]) 58 | 59 | ### Enable features ########################################################################### 60 | AS_IF([test "x$with_vendor" != "xno"],[ 61 | AS_IF([test "x$vendor" = "xyes"],[ 62 | AC_MSG_ERROR([Must supply a valid vendor OID])]) 63 | ],[ 64 | AC_MSG_ERROR([Vendor OID cannot currently be disabled, use default.])]) 65 | AC_DEFINE_UNQUOTED(VENDOR, ["$vendor"], [Vendor OID]) 66 | 67 | AS_IF([test "x$with_config" != "xno"], [ 68 | AC_DEFINE([HAVE_LIBCONFUSE], [1], [Build with support for /etc/mini-snmpd.conf]) 69 | PKG_CHECK_MODULES([confuse], [libconfuse >= 2.7])]) 70 | AM_CONDITIONAL([HAVE_CONFUSE], [test "x$with_config" != "xno"]) 71 | 72 | AS_IF([test "x$enable_debug" = "xyes"],[ 73 | AC_DEFINE(DEBUG, 1, [Define to enable debug mode.])]) 74 | 75 | AS_IF([test "x$enable_demo" = "xyes"],[ 76 | AC_DEFINE(CONFIG_ENABLE_DEMO, 1, [Define to enable demo mode.])]) 77 | 78 | AS_IF([test "x$enable_ipv6" != "xno"],[ 79 | AC_DEFINE(CONFIG_ENABLE_IPV6, 1, [Define to enable IPv6 support.])]) 80 | 81 | AS_IF([test "x$enable_ethtool" != "xno"],[ 82 | AC_DEFINE(CONFIG_ENABLE_ETHTOOL, 1, [Define to enable ethtool stats.])]) 83 | 84 | # Check where to install the systemd .service file 85 | AS_IF([test "x$with_systemd" = "xyes" -o "x$with_systemd" = "xauto"], [ 86 | def_systemd=$($PKG_CONFIG --variable=systemdsystemunitdir systemd) 87 | AS_IF([test "x$def_systemd" = "x"], 88 | [AS_IF([test "x$with_systemd" = "xyes"], 89 | [AC_MSG_ERROR([systemd support requested but pkg-config unable to query systemd package])]) 90 | with_systemd=no], [with_systemd="$def_systemd"])]) 91 | AS_IF([test "x$with_systemd" != "xno"], 92 | [AC_SUBST([systemddir], [$with_systemd])]) 93 | AM_CONDITIONAL([HAVE_SYSTEMD], [test "x$with_systemd" != "xno"]) 94 | 95 | # Expand $sbindir early, into $SBINDIR, for systemd unit file 96 | # NOTE: This does *not* take prefix/exec_prefix override at "make 97 | # install" into account, unfortunately. 98 | test "x$prefix" = xNONE && prefix= 99 | test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' 100 | SBINDIR=`eval echo $sbindir` 101 | SBINDIR=`eval echo $SBINDIR` 102 | AC_SUBST(SBINDIR) 103 | 104 | # Workaround for as-of-yet unreleased runstatedir support, planned for 105 | # autoconf 2.70, which some major distros have backported. 106 | AS_IF([test -z "$runstatedir"], runstatedir="$localstatedir/run") 107 | AC_SUBST(runstatedir) 108 | 109 | ### Generate all files ######################################################################## 110 | AC_OUTPUT 111 | 112 | # Expand directories for configuration summary, unexpanded defaults: 113 | # sysconfdir => ${prefix}/etc 114 | # runstatedir => ${localstatedir}/run 115 | SYSCONFDIR=`eval echo $sysconfdir` 116 | RUNSTATEDIR=`eval echo $runstatedir` 117 | RUNSTATEDIR=`eval echo $RUNSTATEDIR` 118 | 119 | cat < Sun, 23 Feb 2020 08:06:59 +0100 6 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 10 2 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: mini-snmpd 2 | Section: net 3 | Priority: optional 4 | Maintainer: Joachim Nilsson 5 | Build-Depends: debhelper (>= 10), libconfuse-dev, pkg-config, systemd 6 | Standards-Version: 4.3.0 7 | Homepage: https://troglobit.com/mini-snmpd.html 8 | Vcs-Git: git://github.com/troglobit/mini-snmpd.git 9 | Vcs-Browser: https://github.com/troglobit/mini-snmpd 10 | 11 | Package: mini-snmpd 12 | Architecture: any 13 | Depends: ${shlibs:Depends}, ${misc:Depends} 14 | Conflicts: snmpd 15 | Description: Small SNMP daemon 16 | The Mini SNMP daemon is a minimal implementation of an SNMP daemon. It 17 | is primarily targeted at embedded systems with limited disk and memory 18 | resources. All configuration can be done using command line arguments, 19 | or via the optional /etc/mini-snmpd.conf file. It supports basic CPU, 20 | memory, disk, and network interface statistics. 21 | . 22 | mini-snmpd is not as flexibible as, and does not support the same 23 | features as, the de-facto standard net-snmp, but this also means it 24 | does not have the same footprint and overhead. 25 | . 26 | Supported features: 27 | . 28 | * SNMP version 1 and 2c 29 | * Community string authentication when using 2c or explicitly configured 30 | * Read-only access (writing is not supported) 31 | * Basic system info like CPU load, memory, disk and network interfaces 32 | * Does not need a configuration file, but one is supported 33 | * Supports UDP and TCP (thus supports SSH tunneling of SNMP connections) 34 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: mini-snmpd - Small SNMP daemon 3 | Upstream-Contact: Joachim Nilsson 4 | Source: http://github.com/troglobit/mini-snmpd 5 | 6 | Files: * 7 | Copyright: 2008-2010 Robert Ernst 8 | 2015-2020 Joachim Nilsson 9 | License: GPL-2+ 10 | 11 | Files: debian/* 12 | Copyright: 2020 Joachim Nilsson 13 | License: GPL-2+ 14 | -------------------------------------------------------------------------------- /debian/mini-snmpd.default: -------------------------------------------------------------------------------- 1 | # Use this to set command line options, or edit /etc/mini-snmpd.conf 2 | #DAEMON_OPTS="-a -i enp0s25 -i wlp3s0" 3 | -------------------------------------------------------------------------------- /debian/mini-snmpd.init: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ### BEGIN INIT INFO 3 | # Provides: mini-snmpd 4 | # Required-Start: $network $local_fs $remote_fs $syslog 5 | # Required-Stop: $network $local_fs $remote_fs $syslog 6 | # Default-Start: 2 3 4 5 7 | # Default-Stop: 0 1 6 8 | # Short-Description: Small SNMP daemon 9 | # Description: Small SNMP daemon targeted at resource limited systems 10 | ### END INIT INFO 11 | 12 | PATH=/sbin:/usr/sbin:/bin:/usr/bin 13 | DESC="Small SNMP damon" 14 | NAME=mini-snmpd 15 | DAEMON=/usr/sbin/$NAME 16 | RUNDIR=/run/$NAME 17 | CACHEDIR=/var/cache/$NAME 18 | PIDFILE=/$RUNDIR/$NAME.pid 19 | SCRIPTNAME=/etc/init.d/$NAME 20 | DEFAULT=/etc/default/$NAME 21 | 22 | [ -x $DAEMON ] || exit 0 23 | [ -r $DEFAULT ] && . $DEFAULT 24 | 25 | . /lib/lsb/init-functions 26 | 27 | do_start() 28 | { 29 | start-stop-daemon --start --quiet --test \ 30 | --pidfile $PIDFILE --exec $DAEMON > /dev/null \ 31 | || return 1 32 | start-stop-daemon --start --quiet \ 33 | --pidfile $PIDFILE --exec $DAEMON -- $DAEMON_OPTS \ 34 | || return 2 35 | } 36 | 37 | do_stop() 38 | { 39 | start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 \ 40 | --pidfile $PIDFILE --name $NAME 41 | RETVAL="$?" 42 | [ "$RETVAL" = 2 ] && return 2 43 | rm -f $PIDFILE 44 | return "$RETVAL" 45 | } 46 | 47 | do_reload() { 48 | start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME 49 | return 0 50 | } 51 | 52 | case "$1" in 53 | start) 54 | log_daemon_msg "Starting $DESC " "$NAME" 55 | do_start 56 | case "$?" in 57 | 0|1) log_end_msg 0 ;; 58 | 2) log_end_msg 1 ;; 59 | esac 60 | ;; 61 | stop) 62 | log_daemon_msg "Stopping $DESC" "$NAME" 63 | do_stop 64 | case "$?" in 65 | 0|1) log_end_msg 0 ;; 66 | 2) log_end_msg 1 ;; 67 | esac 68 | ;; 69 | status) 70 | status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $? 71 | ;; 72 | reload|force-reload) 73 | log_daemon_msg "Reloading $DESC" "$NAME" 74 | do_reload 75 | log_end_msg $? 76 | ;; 77 | restart) 78 | log_daemon_msg "Restarting $DESC" "$NAME" 79 | do_stop 80 | case "$?" in 81 | 0|1) 82 | do_start 83 | case "$?" in 84 | 0) log_end_msg 0 ;; 85 | 1) log_end_msg 1 ;; # Old process is still running 86 | *) log_end_msg 1 ;; # Failed to start 87 | esac 88 | ;; 89 | *) 90 | # Failed to stop 91 | log_end_msg 1 92 | ;; 93 | esac 94 | ;; 95 | *) 96 | echo "Usage: $SCRIPTNAME {start|stop|status|restart|reload|force-reload}" >&2 97 | exit 3 98 | ;; 99 | esac 100 | 101 | : 102 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # export DH_VERBOSE=1 3 | export DEB_BUILD_MAINT_OPTIONS = hardening=+all 4 | export DEB_CFLAGS_MAINT_APPEND = -W -Wall -Wextra -O3 5 | export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed 6 | 7 | NUMJOBS = $(patsubst parallel=%,%,$(filter parallel=%,$(DEB_BUILD_OPTIONS))) 8 | 9 | include /usr/share/dpkg/default.mk # provides DEB_VERSION 10 | 11 | %: 12 | dh $@ --with=systemd 13 | 14 | override_dh_shlibdeps: 15 | dh_shlibdeps --dpkg-shlibdeps-params=--ignore-missing-info 16 | 17 | override_dh_installchangelogs: 18 | dh_installchangelogs ChangeLog.md 19 | 20 | override_dh_auto_install: 21 | dh_auto_install 22 | rm -f debian/mini-snmpd/usr/share/doc/mini-snmpd/COPYING 23 | 24 | override_dh_installinit: 25 | dh_systemd_enable 26 | dh_installinit 27 | dh_systemd_start --no-restart-on-upgrade 28 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (native) 2 | -------------------------------------------------------------------------------- /ethtool-conf.h: -------------------------------------------------------------------------------- 1 | /* Linux ethtool helpers 2 | * 3 | * Copyright (C) 2020 Bjørn Mork 4 | * 5 | * This file may be distributed and/or modified under the terms of the 6 | * GNU General Public License version 2 as published by the Free Software 7 | * Foundation and appearing in the file LICENSE.GPL included in the 8 | * packaging of this file. 9 | * 10 | * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE 11 | * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 12 | * 13 | * See COPYING for GPL licensing information. 14 | */ 15 | 16 | #ifndef ETHTOOL_CONF_H_ 17 | #define ETHTOOL_CONF_H_ 18 | 19 | #include 20 | #include "config.h" 21 | #include "mini-snmpd.h" 22 | 23 | #ifdef CONFIG_ENABLE_ETHTOOL 24 | void ethtool_xlate_cfg(cfg_t *cfg); 25 | #else 26 | static inline void ethtool_xlate_cfg(cfg_t *cfg) 27 | { 28 | if (cfg_size(cfg, "ethtool") > 0) 29 | logit(LOG_WARNING, 0, "No ethtool support. Ignoring config section"); 30 | } 31 | #endif 32 | 33 | #endif /* ETHTOOL_CONF_H_ */ 34 | 35 | /* vim: ts=4 sts=4 sw=4 nowrap 36 | */ 37 | 38 | -------------------------------------------------------------------------------- /freebsd.c: -------------------------------------------------------------------------------- 1 | /* FreeBSD backend 2 | * 3 | * Copyright (C) 2008-2010 Robert Ernst 4 | * Copyright (C) 2015-2020 Joachim Nilsson 5 | * 6 | * This file may be distributed and/or modified under the terms of the 7 | * GNU General Public License version 2 as published by the Free Software 8 | * Foundation and appearing in the file LICENSE.GPL included in the 9 | * packaging of this file. 10 | * 11 | * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE 12 | * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 13 | * 14 | * See COPYING for GPL licensing information. 15 | */ 16 | #ifdef __FreeBSD__ 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | 51 | #include "mini-snmpd.h" 52 | 53 | unsigned int get_process_uptime(void) 54 | { 55 | static unsigned int uptime_start = 0; 56 | unsigned int uptime_now = get_system_uptime(); 57 | 58 | if (uptime_start == 0) 59 | uptime_start = uptime_now; 60 | 61 | return uptime_now - uptime_start; 62 | } 63 | 64 | unsigned int get_system_uptime(void) 65 | { 66 | #if 1 67 | struct timespec tv; 68 | 69 | if (clock_gettime(CLOCK_UPTIME_PRECISE, &tv)) 70 | return -1; 71 | 72 | return tv.tv_sec; 73 | #else 74 | int mib[2] = { CTL_KERN, KERN_BOOTTIME }; 75 | size_t len; 76 | struct timeval uptime; 77 | 78 | len = sizeof(uptime); 79 | if (0 != sysctl(mib, 2, &uptime, &len, NULL, 0)) 80 | return -1; 81 | 82 | return time(NULL) - uptime.tv_sec; 83 | #endif 84 | } 85 | 86 | void get_loadinfo(loadinfo_t *loadinfo) 87 | { 88 | struct loadavg avgs; 89 | size_t len = sizeof(avgs); 90 | int i, mib[2] = { CTL_VM, VM_LOADAVG }; 91 | 92 | memset(loadinfo, 0, sizeof(loadinfo_t)); 93 | if (sysctl(mib, 2, &avgs, &len, NULL, 0)) 94 | return; 95 | 96 | for (i = 0; i < 3; i++) 97 | loadinfo->avg[i] = (float)avgs.ldavg[i] / (float)avgs.fscale * 100; 98 | } 99 | 100 | void get_meminfo(meminfo_t *meminfo) 101 | { 102 | struct vmtotal vmt; 103 | unsigned long physmem, cache_cnt = 0; 104 | unsigned int pagesize; 105 | size_t len; 106 | int ret = 0, mib[2] = { CTL_HW, HW_PHYSMEM }; 107 | 108 | memset(meminfo, 0, sizeof(meminfo_t)); 109 | 110 | len = sizeof(physmem); 111 | ret = sysctl(mib, 2, &physmem, &len, NULL, 0); 112 | if (ret) { 113 | perror("hw.physmem"); 114 | return; 115 | } 116 | 117 | mib[1] = HW_PAGESIZE; 118 | ret = sysctl(mib, 2, &pagesize, &len, NULL, 0); 119 | if (ret) { 120 | perror("hw.pagesize"); 121 | return; 122 | } 123 | 124 | mib[0] = CTL_VM; mib[1] = VM_TOTAL; 125 | len = sizeof(vmt); 126 | ret = sysctl(mib, 2, &vmt, &len, NULL, 0); 127 | if (ret) { 128 | perror("vm.total"); 129 | return; 130 | } 131 | 132 | meminfo->total = (unsigned int)physmem / 1024; 133 | meminfo->free = vmt.t_free * pagesize / 1024; 134 | meminfo->shared = vmt.t_vmshr; /* avmshr or vmrshr */ 135 | meminfo->buffers = 0; /* Not on FreeBSD? */ 136 | len = sizeof(cache_cnt); 137 | sysctlbyname("vm.stats.vm.v_cache_count", &cache_cnt, &len, NULL, 0); 138 | meminfo->cached = (unsigned int)cache_cnt * pagesize / 1024; 139 | } 140 | 141 | void get_cpuinfo(cpuinfo_t *cpuinfo) 142 | { 143 | long cp_info[CPUSTATES]; 144 | size_t len = sizeof(cp_info); 145 | 146 | memset(cpuinfo, 0, sizeof(*cpuinfo)); 147 | if (sysctlbyname("kern.cp_time", &cp_info, &len, NULL, 0) < 0) 148 | return; 149 | 150 | cpuinfo->user = cp_info[CP_USER]; 151 | cpuinfo->nice = cp_info[CP_NICE]; 152 | cpuinfo->system = cp_info[CP_SYS]; 153 | cpuinfo->idle = cp_info[CP_IDLE]; 154 | cpuinfo->irqs = cp_info[CP_INTR]; 155 | cpuinfo->cntxts = 0; /* TODO */ 156 | } 157 | 158 | void get_ipinfo(ipinfo_t *ipinfo) 159 | { 160 | size_t len; 161 | 162 | memset(ipinfo, 0, sizeof(ipinfo_t)); 163 | 164 | len = sizeof(ipinfo->ipForwarding); 165 | sysctlbyname("net.inet.ip.forwarding", &ipinfo->ipForwarding, &len, NULL, 0); 166 | 167 | len = sizeof(ipinfo->ipDefaultTTL); 168 | sysctlbyname("net.inet.ip.ttl", &ipinfo->ipDefaultTTL, &len, NULL, 0); 169 | 170 | ipinfo->ipReasmTimeout = IPFRAGTTL; 171 | } 172 | 173 | void get_tcpinfo(tcpinfo_t *tcpinfo) 174 | { 175 | struct clockinfo clockinfo; 176 | struct tcpstat tcps; 177 | size_t len; 178 | 179 | memset(tcpinfo, 0, sizeof(*tcpinfo)); 180 | 181 | len = sizeof(clockinfo); 182 | if (sysctlbyname("kern.clockrate", &clockinfo, &len, NULL, 0) == -1) 183 | return; 184 | if (len != sizeof(clockinfo)) 185 | return; 186 | 187 | len = sizeof(tcps); 188 | if (sysctlbyname("net.inet.tcp.stats", &tcps, &len, NULL, 0) == -1) 189 | return; 190 | 191 | if (sizeof(tcps) != len) 192 | return; 193 | 194 | tcpinfo->tcpRtoAlgorithm = 4; /* Van Jacobson */ 195 | #define hz clockinfo.hz 196 | tcpinfo->tcpRtoMin = 1000 * TCPTV_MIN / hz; 197 | tcpinfo->tcpRtoMax = 1000 * TCPTV_REXMTMAX / hz; 198 | #undef hz 199 | tcpinfo->tcpMaxConn = -1; 200 | tcpinfo->tcpActiveOpens = tcps.tcps_connattempt; 201 | tcpinfo->tcpPassiveOpens = tcps.tcps_accepts; 202 | tcpinfo->tcpAttemptFails = tcps.tcps_conndrops; 203 | tcpinfo->tcpEstabResets = tcps.tcps_drops; 204 | tcpinfo->tcpCurrEstab = tcps.tcps_connects + tcps.tcps_closed; 205 | tcpinfo->tcpInSegs = tcps.tcps_rcvtotal; 206 | tcpinfo->tcpOutSegs = tcps.tcps_sndtotal - tcps.tcps_sndrexmitpack; 207 | tcpinfo->tcpRetransSegs = tcps.tcps_sndrexmitpack; 208 | tcpinfo->tcpInErrs = tcps.tcps_rcvbadsum + tcps.tcps_rcvbadoff + tcps.tcps_rcvshort; 209 | tcpinfo->tcpOutRsts = tcps.tcps_sndctrl; /* Not just sent RSTs, includes SYN + FIN */ 210 | } 211 | 212 | void get_udpinfo(udpinfo_t *udpinfo) 213 | { 214 | struct udpstat udps; 215 | size_t len = sizeof(udps); 216 | 217 | memset(udpinfo, 0, sizeof(*udpinfo)); 218 | 219 | if (sysctlbyname("net.inet.udp.stats", &udps, &len, NULL, 0) == -1) 220 | return; 221 | 222 | if (sizeof(udps) != len) 223 | return; 224 | 225 | udpinfo->udpInDatagrams = udps.udps_ipackets; 226 | udpinfo->udpNoPorts = (udps.udps_noport + 227 | udps.udps_noportbcast + 228 | udps.udps_noportmcast); 229 | udpinfo->udpInErrors = (udps.udps_hdrops + 230 | udps.udps_badsum + 231 | udps.udps_badlen); 232 | udpinfo->udpOutDatagrams = udps.udps_opackets; 233 | } 234 | 235 | void get_diskinfo(diskinfo_t *diskinfo) 236 | { 237 | struct statfs fs; 238 | size_t i; 239 | 240 | memset(diskinfo, 0, sizeof(*diskinfo)); 241 | for (i = 0; i < g_disk_list_length; i++) { 242 | if (statfs(g_disk_list[i], &fs) == -1) 243 | continue; 244 | 245 | diskinfo->total[i] = ((float)fs.f_blocks * fs.f_bsize) / 1024; 246 | diskinfo->free[i] = ((float)fs.f_bfree * fs.f_bsize) / 1024; 247 | diskinfo->used[i] = ((float)(fs.f_blocks - fs.f_bfree) * fs.f_bsize) / 1024; 248 | diskinfo->blocks_used_percent[i] = 249 | ((float)(fs.f_blocks - fs.f_bfree) * 100 + fs.f_blocks - 1) / fs.f_blocks; 250 | if (fs.f_files <= 0) 251 | diskinfo->inodes_used_percent[i] = 0; 252 | else 253 | diskinfo->inodes_used_percent[i] = 254 | ((float)(fs.f_files - fs.f_ffree) * 100 + fs.f_files - 1) / fs.f_files; 255 | } 256 | } 257 | 258 | void get_netinfo(netinfo_t *netinfo) 259 | { 260 | struct ifaddrs *ifap, *ifa; 261 | 262 | memset(netinfo, 0, sizeof(*netinfo)); 263 | if (getifaddrs(&ifap) < 0) 264 | return; 265 | 266 | for (ifa = ifap; ifa; ifa = ifa->ifa_next) { 267 | struct sockaddr_in *addr, *mask, *bcaddr; 268 | struct sockaddr_dl *sdl; 269 | struct if_data *ifd; 270 | int i; 271 | 272 | if (!ifa->ifa_addr) 273 | continue; 274 | 275 | i = find_ifname(ifa->ifa_name); 276 | if (i == -1) 277 | continue; 278 | 279 | switch (ifa->ifa_addr->sa_family) { 280 | case AF_LINK: 281 | sdl = (struct sockaddr_dl *)ifa->ifa_addr; 282 | memcpy(netinfo->mac_addr[i], LLADDR(sdl), sizeof(netinfo->mac_addr[i])); 283 | 284 | ifd = ifa->ifa_data; 285 | if (!ifd) 286 | continue; 287 | 288 | if (ifa->ifa_flags & IFF_UP) 289 | switch (ifd->ifi_link_state) { 290 | case LINK_STATE_UP: 291 | netinfo->status[i] = 1; 292 | break; 293 | case LINK_STATE_DOWN: 294 | netinfo->status[i] = 7; 295 | break; 296 | default: 297 | case LINK_STATE_UNKNOWN: 298 | netinfo->status[i] = 4; 299 | break; 300 | } 301 | else 302 | netinfo->status[i] = 2; /* Down */ 303 | 304 | switch (ifd->ifi_type) { 305 | default: 306 | case IFT_ETHER: 307 | netinfo->if_type[i] = 6; /* ethernetCsmacd(6) */ 308 | break; 309 | case IFT_PPP: 310 | netinfo->if_type[i] = 23; /* ppp(23) */ 311 | break; 312 | case IFT_LOOP: 313 | netinfo->if_type[i] = 24; /* softwareLoopback(24) */ 314 | break; 315 | case IFT_SLIP: 316 | netinfo->if_type[i] = 28; /* slip(28) */ 317 | break; 318 | } 319 | 320 | netinfo->if_mtu[i] = ifd->ifi_mtu; 321 | netinfo->if_speed[i] = ifd->ifi_baudrate; 322 | netinfo->ifindex[i] = if_nametoindex(ifa->ifa_name); 323 | netinfo->lastchange[1] = ifd->ifi_lastchange.tv_sec; 324 | netinfo->rx_bytes[i] = ifd->ifi_ibytes; 325 | netinfo->rx_packets[i] = ifd->ifi_ipackets; 326 | netinfo->rx_mc_packets[i] = ifd->ifi_imcasts; 327 | netinfo->rx_bc_packets[i] = 0; /* XXX: Couldn't find at first glance */ 328 | netinfo->rx_errors[i] = ifd->ifi_ierrors; 329 | netinfo->rx_drops[i] = ifd->ifi_iqdrops; 330 | netinfo->tx_bytes[i] = ifd->ifi_obytes; 331 | netinfo->tx_packets[i] = ifd->ifi_opackets; 332 | netinfo->tx_mc_packets[i] = ifd->ifi_omcasts; 333 | netinfo->tx_bc_packets[i] = 0; /* XXX: Couldn't find at first glance */ 334 | netinfo->tx_errors[i] = ifd->ifi_oerrors; 335 | netinfo->tx_drops[i] = ifd->ifi_collisions; 336 | break; 337 | 338 | case AF_INET: 339 | if (!ifa->ifa_addr || !ifa->ifa_netmask) 340 | continue; 341 | 342 | addr = (struct sockaddr_in *)ifa->ifa_addr; 343 | mask = (struct sockaddr_in *)ifa->ifa_netmask; 344 | if (addr) { 345 | netinfo->in_addr[i] = ntohl(addr->sin_addr.s_addr); 346 | netinfo->in_mask[i] = ntohl(mask->sin_addr.s_addr); 347 | } 348 | 349 | bcaddr = (struct sockaddr_in *)ifa->ifa_broadaddr; 350 | if (bcaddr && (ifa->ifa_flags & IFF_BROADCAST)) { 351 | netinfo->in_bcaddr[i] = ntohl(bcaddr->sin_addr.s_addr); 352 | netinfo->in_bcent[i] = netinfo->in_bcaddr[i] ? 1 : 0; 353 | } 354 | break; 355 | } 356 | } 357 | 358 | freeifaddrs(ifap); 359 | } 360 | 361 | #endif /* __FreeBSD__ */ 362 | 363 | /* vim: ts=4 sts=4 sw=4 nowrap 364 | */ 365 | -------------------------------------------------------------------------------- /globals.c: -------------------------------------------------------------------------------- 1 | /* Global variables 2 | * 3 | * Copyright (C) 2008-2010 Robert Ernst 4 | * Copyright (C) 2015-2020 Joachim Nilsson 5 | * 6 | * This file may be distributed and/or modified under the terms of the 7 | * GNU General Public License version 2 as published by the Free Software 8 | * Foundation and appearing in the file LICENSE.GPL included in the 9 | * packaging of this file. 10 | * 11 | * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE 12 | * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 13 | * 14 | * See COPYING for GPL licensing information. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "mini-snmpd.h" 24 | 25 | const struct in_addr inaddr_any = { INADDR_ANY }; 26 | 27 | int g_family = AF_INET; 28 | int g_timeout = 1; 29 | int g_auth = 0; 30 | int g_daemon = 1; 31 | int g_syslog = 0; 32 | int g_level = LOG_NOTICE; 33 | volatile sig_atomic_t g_quit = 0; 34 | 35 | char *g_prognm; 36 | char *g_community; 37 | char *g_vendor; 38 | char *g_description; 39 | char *g_location; 40 | char *g_contact; 41 | char *g_bind_to_device; 42 | char *g_user; 43 | 44 | char *g_disk_list[MAX_NR_DISKS] = { "/" }; 45 | size_t g_disk_list_length = 1; 46 | 47 | char *g_interface_list[MAX_NR_INTERFACES]; 48 | size_t g_interface_list_length; 49 | 50 | in_port_t g_udp_port = 161; 51 | in_port_t g_tcp_port = 161; 52 | 53 | int g_udp_sockfd = -1; 54 | int g_tcp_sockfd = -1; 55 | 56 | client_t g_udp_client; 57 | client_t *g_tcp_client_list[MAX_NR_CLIENTS]; 58 | size_t g_tcp_client_list_length; 59 | 60 | value_t g_mib[MAX_NR_VALUES]; 61 | size_t g_mib_length; 62 | 63 | /* vim: ts=4 sts=4 sw=4 nowrap 64 | */ 65 | -------------------------------------------------------------------------------- /java/.cvsignore: -------------------------------------------------------------------------------- 1 | *.class 2 | *.jar 3 | -------------------------------------------------------------------------------- /java/Makefile: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------------------------------------ 2 | # Copyright (C) 2008 Robert Ernst 3 | # 4 | # This file may be distributed and/or modified under the terms of the 5 | # GNU General Public License version 2 as published by the Free Software 6 | # Foundation and appearing in the file LICENSE.GPL included in the 7 | # packaging of this file. 8 | # 9 | # This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE 10 | # WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 11 | # 12 | # See COPYING for GPL licensing information. 13 | # 14 | 15 | UdpToTcp.class: UdpToTcp.java 16 | javac UdpToTcp.java 17 | 18 | clean: 19 | rm -f *.class 20 | 21 | -------------------------------------------------------------------------------- /java/README: -------------------------------------------------------------------------------- 1 | This program is just a quick and dirty hack to get mini-snmpd accessible over 2 | an SSH tunnel for users that run their SSH server on Windows. It is by no means 3 | a replacement for more mature and robust software tools. If you are running a 4 | Unix-like operating system, socat (http://www.dest-unreach.org/socat/) should 5 | be a better solution. 6 | 7 | Robert Ernst . 8 | -------------------------------------------------------------------------------- /java/UdpToTcp.8: -------------------------------------------------------------------------------- 1 | .TH UdpToTcp 8 2 | .\" NAME should be all caps, SECTION should be 1-8, maybe w/ subsection 3 | .\" other parms are allowed: see man(7), man(1) 4 | .SH NAME 5 | UdpToTcp \- a minimal implementation of an UDP to TCP forwarder. 6 | .SH SYNOPSIS 7 | .B java -cp . UdpToTcp.class 8 | .I "options" 9 | .SH "DESCRIPTION" 10 | .B UdpToTcp 11 | is a program that can be used to forward packets sent to an UDP port to a TCP 12 | connection and vice versa. This is needed if you want to tunnel UDP traffic 13 | over a TCP connection. It was written to support UDP-only SNMP clients 14 | accessing a 15 | .I mini-snmpd 16 | server that can only be reached over an SSH tunnel. 17 | .TP 18 | .I -udp-port:nnn 19 | The UDP port where the program listens (default is 161) 20 | .TP 21 | .I -port:nnn 22 | The TCP port where the program connects to (default is 161) 23 | .TP 24 | .I -host:nnn 25 | The hostname where the program connects to (default is localhost) 26 | .PP 27 | .B UdpToTcp 28 | can be told to exit by entering 29 | .I Ctrl-C 30 | .SH "EXAMPLES" 31 | java -cp . UdpToTcp -udp-port:9020 -host:remote.somewhere.net 32 | .PP 33 | This command starts the program and tells it to forward UDP traffic received 34 | on the local port 9020 to the remote host. Thus, you now can point an SNMP 35 | client only supporting UDP to localhost:9020 and it will query SNMP data from 36 | remote.somewhere.net (this assumes that the SNMP daemon on the remote host 37 | answers requests over TCP too). 38 | .SH "AUTHOR" 39 | This manual page was written by Robert Ernst . 40 | -------------------------------------------------------------------------------- /java/UdpToTcp.java: -------------------------------------------------------------------------------- 1 | /* ----------------------------------------------------------------------------- 2 | * Copyright (C) 2008 Robert Ernst 3 | * 4 | * This file may be distributed and/or modified under the terms of the 5 | * GNU General Public License version 2 as published by the Free Software 6 | * Foundation and appearing in the file LICENSE.GPL included in the 7 | * packaging of this file. 8 | * 9 | * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE 10 | * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 11 | * 12 | * See COPYING for GPL licensing information. 13 | */ 14 | 15 | import java.net.DatagramSocket; 16 | import java.net.DatagramPacket; 17 | import java.net.InetAddress; 18 | import java.net.Socket; 19 | import java.net.SocketException; 20 | import java.io.InputStream; 21 | import java.io.OutputStream; 22 | 23 | public final class UdpToTcp { 24 | public static void main(String[] args) { 25 | String host = null; 26 | Integer port = 161; 27 | Integer udp_port = 161; 28 | Socket tcp_socket = localhost; 29 | DatagramSocket udp_socket = null; 30 | InputStream input_stream = null; 31 | OutputStream output_stream = null; 32 | 33 | /* Parse commandline arguments */ 34 | try { 35 | for (int i = 0; i < args.length; i++) { 36 | int pos =args[i].indexOf(':'); 37 | if (args[i].matches("-udp-port:[0-9]+")) { 38 | udp_port = Integer.parseInt(args[i].substring(pos + 1)); 39 | } else if (args[i].matches("-port:[0-9]+")) { 40 | port = Integer.parseInt(args[i].substring(pos + 1)); 41 | } else if (args[i].matches("-host:.+")) { 42 | host = args[i].substring(pos + 1); 43 | } 44 | } 45 | } catch (Exception e) { 46 | System.out.println(e); 47 | System.exit(1); 48 | } 49 | 50 | /* Open the local UDP socket */ 51 | try { 52 | udp_socket = new DatagramSocket(udp_port); 53 | } catch (Exception e) { 54 | System.out.println(e); 55 | System.exit(1); 56 | } 57 | 58 | /* Open the remote TCP socket */ 59 | try { 60 | tcp_socket = new Socket(host, port); 61 | input_stream = tcp_socket.getInputStream(); 62 | output_stream = tcp_socket.getOutputStream(); 63 | } catch (Exception e) { 64 | System.out.println(e); 65 | System.exit(1); 66 | } 67 | 68 | /* Forward until the hell freezes */ 69 | System.out.println("forwarding udp:localhost:" + udp_port 70 | + " to tcp:" + host + ":" + port); 71 | while (true) { 72 | DatagramPacket packet = null; 73 | byte[] buffer = new byte[2048]; 74 | 75 | /* Receive the UDP packet */ 76 | try { 77 | packet = new DatagramPacket(buffer, buffer.length); 78 | udp_socket.receive(packet); 79 | } catch (Exception e) { 80 | System.out.println(e); 81 | System.exit(1); 82 | } 83 | System.out.println("received UDP packet of " + packet.getLength() + " bytes"); 84 | 85 | /* Send the TCP packet */ 86 | try { 87 | output_stream.write(packet.getData(), 0, packet.getLength()); 88 | output_stream.flush(); 89 | } catch (Exception e) { 90 | System.out.println(e); 91 | System.exit(1); 92 | } 93 | System.out.println("sent TCP packet"); 94 | 95 | /* Receive the TCP packet */ 96 | try { 97 | int length = input_stream.read(buffer); 98 | packet.setData(buffer); 99 | packet.setLength(length); 100 | } catch (Exception e) { 101 | System.out.println(e); 102 | System.exit(1); 103 | } 104 | System.out.println("received TCP packet of " + packet.getLength() + " bytes"); 105 | 106 | /* Send the UDP packet */ 107 | try { 108 | udp_socket.send(packet); 109 | } catch (Exception e) { 110 | System.out.println(e); 111 | System.exit(1); 112 | } 113 | System.out.println("sent UDP packet"); 114 | } 115 | } 116 | } 117 | 118 | /* vim: ts=4 sw=4 sts=4 nowrap 119 | */ 120 | -------------------------------------------------------------------------------- /lib/pidfile.c: -------------------------------------------------------------------------------- 1 | /* Updated by troglobit for libite/finit/uftpd projects 2016/07/04 */ 2 | /* $OpenBSD: pidfile.c,v 1.11 2015/06/03 02:24:36 millert Exp $ */ 3 | /* $NetBSD: pidfile.c,v 1.4 2001/02/19 22:43:42 cgd Exp $ */ 4 | 5 | /*- 6 | * Copyright (c) 1999 The NetBSD Foundation, Inc. 7 | * All rights reserved. 8 | * 9 | * This code is derived from software contributed to The NetBSD Foundation 10 | * by Jason R. Thorpe. 11 | * 12 | * Redistribution and use in source and binary forms, with or without 13 | * modification, are permitted provided that the following conditions 14 | * are met: 15 | * 1. Redistributions of source code must retain the above copyright 16 | * notice, this list of conditions and the following disclaimer. 17 | * 2. Redistributions in binary form must reproduce the above copyright 18 | * notice, this list of conditions and the following disclaimer in the 19 | * documentation and/or other materials provided with the distribution. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 22 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 23 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 24 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 25 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | * POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #define _GNU_SOURCE /* Needed with GLIBC to get asprintf() */ 35 | #include /* utimensat() */ 36 | #include /* utimensat() on *BSD */ 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include "compat.h" 43 | 44 | static char *pidfile_path = NULL; 45 | static pid_t pidfile_pid = 0; 46 | 47 | static void pidfile_cleanup(void); 48 | 49 | const char *__pidfile_path = RUNSTATEDIR; 50 | const char *__pidfile_name = NULL; 51 | 52 | int 53 | pidfile(const char *basename) 54 | { 55 | int save_errno; 56 | int atexit_already; 57 | pid_t pid; 58 | FILE *f; 59 | 60 | if (basename == NULL) 61 | basename = getprogname(); 62 | 63 | pid = getpid(); 64 | atexit_already = 0; 65 | 66 | if (pidfile_path != NULL) { 67 | if (!access(pidfile_path, R_OK) && pid == pidfile_pid) { 68 | utimensat(0, pidfile_path, NULL, 0); 69 | return (0); 70 | } 71 | free(pidfile_path); 72 | pidfile_path = NULL; 73 | __pidfile_name = NULL; 74 | atexit_already = 1; 75 | } 76 | 77 | if (basename[0] != '/') { 78 | if (asprintf(&pidfile_path, "%s/%s.pid", __pidfile_path, basename) == -1) 79 | return (-1); 80 | } else { 81 | if (asprintf(&pidfile_path, "%s", basename) == -1) 82 | return (-1); 83 | } 84 | 85 | if ((f = fopen(pidfile_path, "w")) == NULL) { 86 | save_errno = errno; 87 | free(pidfile_path); 88 | pidfile_path = NULL; 89 | errno = save_errno; 90 | return (-1); 91 | } 92 | 93 | if (fprintf(f, "%ld\n", (long)pid) <= 0 || fflush(f) != 0) { 94 | save_errno = errno; 95 | (void) fclose(f); 96 | (void) unlink(pidfile_path); 97 | free(pidfile_path); 98 | pidfile_path = NULL; 99 | errno = save_errno; 100 | return (-1); 101 | } 102 | (void) fclose(f); 103 | __pidfile_name = pidfile_path; 104 | 105 | /* 106 | * LITE extension, no need to set up another atexit() handler 107 | * if user only called us to update the mtime of the PID file 108 | */ 109 | if (atexit_already) 110 | return (0); 111 | 112 | pidfile_pid = pid; 113 | if (atexit(pidfile_cleanup) < 0) { 114 | save_errno = errno; 115 | (void) unlink(pidfile_path); 116 | free(pidfile_path); 117 | pidfile_path = NULL; 118 | pidfile_pid = 0; 119 | errno = save_errno; 120 | return (-1); 121 | } 122 | 123 | return (0); 124 | } 125 | 126 | static void 127 | pidfile_cleanup(void) 128 | { 129 | if (pidfile_path != NULL && pidfile_pid == getpid()) { 130 | (void) unlink(pidfile_path); 131 | free(pidfile_path); 132 | pidfile_path = NULL; 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /lib/utimensat.c: -------------------------------------------------------------------------------- 1 | /* Replacement in case utimensat(2) is missing 2 | * 3 | * Copyright (C) 2017-2018 Joachim Nilsson 4 | * 5 | * Permission to use, copy, modify, and/or distribute this software for any 6 | * purpose with or without fee is hereby granted, provided that the above 7 | * copyright notice and this permission notice appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | */ 17 | 18 | #include 19 | #ifdef HAVE_FCNTL_H 20 | #include 21 | #endif 22 | #include /* lutimes(), utimes(), utimensat() */ 23 | 24 | int 25 | utimensat(int dirfd, const char *pathname, const struct timespec ts[2], int flags) 26 | { 27 | int ret = -1; 28 | struct timeval tv[2]; 29 | 30 | if (dirfd != 0) { 31 | errno = ENOTSUP; 32 | return -1; 33 | } 34 | 35 | TIMESPEC_TO_TIMEVAL(&tv[0], &ts[0]); 36 | TIMESPEC_TO_TIMEVAL(&tv[1], &ts[1]); 37 | 38 | #ifdef AT_SYMLINK_NOFOLLOW 39 | if ((flags & AT_SYMLINK_NOFOLLOW) == AT_SYMLINK_NOFOLLOW) 40 | ret = lutimes(pathname, tv); 41 | else 42 | #endif 43 | ret = utimes(pathname, tv); 44 | 45 | return ret; 46 | } 47 | -------------------------------------------------------------------------------- /linux.c: -------------------------------------------------------------------------------- 1 | /* Linux backend 2 | * 3 | * Copyright (C) 2008-2010 Robert Ernst 4 | * Copyright (C) 2015-2020 Joachim Nilsson 5 | * 6 | * This file may be distributed and/or modified under the terms of the 7 | * GNU General Public License version 2 as published by the Free Software 8 | * Foundation and appearing in the file LICENSE.GPL included in the 9 | * packaging of this file. 10 | * 11 | * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE 12 | * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 13 | * 14 | * See COPYING for GPL licensing information. 15 | */ 16 | #ifdef __linux__ 17 | 18 | #include 19 | #include 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 | 39 | #include "mini-snmpd.h" 40 | 41 | 42 | /* We need the uptime in 1/100 seconds, so we can't use sysinfo() */ 43 | unsigned int get_process_uptime(void) 44 | { 45 | static unsigned int uptime_start = 0; 46 | unsigned int uptime_now = get_system_uptime(); 47 | 48 | if (uptime_start == 0) 49 | uptime_start = uptime_now; 50 | 51 | return uptime_now - uptime_start; 52 | } 53 | 54 | /* We need the uptime in 1/100 seconds, so we can't use sysinfo() */ 55 | unsigned int get_system_uptime(void) 56 | { 57 | char buf[128]; 58 | 59 | if (read_file("/proc/uptime", buf, sizeof(buf)) == -1) 60 | return -1; 61 | 62 | return (unsigned int)(atof(buf) * 100); 63 | } 64 | 65 | void get_loadinfo(loadinfo_t *loadinfo) 66 | { 67 | char buf[128]; 68 | char *ptr; 69 | int i; 70 | 71 | memset(loadinfo, 0, sizeof(loadinfo_t)); 72 | if (read_file("/proc/loadavg", buf, sizeof(buf)) == -1) 73 | return; 74 | 75 | ptr = buf; 76 | for (i = 0; i < 3; i++) { 77 | while (isspace(*ptr)) 78 | ptr++; 79 | 80 | if (*ptr == 0) 81 | continue; 82 | 83 | loadinfo->avg[i] = strtod(ptr, &ptr) * 100; 84 | } 85 | } 86 | 87 | void get_meminfo(meminfo_t *meminfo) 88 | { 89 | field_t fields[] = { 90 | { "MemTotal", 1, { &meminfo->total }}, 91 | { "MemFree", 1, { &meminfo->free }}, 92 | { "MemShared", 1, { &meminfo->shared }}, 93 | { "Buffers", 1, { &meminfo->buffers }}, 94 | { "Cached", 1, { &meminfo->cached }}, 95 | }; 96 | 97 | memset(meminfo, 0, sizeof(meminfo_t)); 98 | parse_file("/proc/meminfo", fields, NELEMS(fields), 0); 99 | } 100 | 101 | void get_cpuinfo(cpuinfo_t *cpuinfo) 102 | { 103 | field_t fields[] = { 104 | { "cpu ", 4, { &cpuinfo->user, &cpuinfo->nice, &cpuinfo->system, &cpuinfo->idle }}, 105 | { "intr ", 1, { &cpuinfo->irqs }}, 106 | { "ctxt ", 1, { &cpuinfo->cntxts }}, 107 | }; 108 | 109 | memset(cpuinfo, 0, sizeof(cpuinfo_t)); 110 | parse_file("/proc/stat", fields, NELEMS(fields), 0); 111 | } 112 | 113 | void get_ipinfo(ipinfo_t *ipinfo) 114 | { 115 | long long garbage; 116 | field_t fields[] = { 117 | { "Ip", 13, 118 | { &ipinfo->ipForwarding, 119 | &ipinfo->ipDefaultTTL, 120 | &garbage, 121 | &garbage, 122 | &garbage, 123 | &garbage, 124 | &garbage, 125 | &garbage, 126 | &garbage, 127 | &garbage, 128 | &garbage, 129 | &garbage, 130 | &ipinfo->ipReasmTimeout } }, 131 | }; 132 | 133 | memset(ipinfo, 0, sizeof(ipinfo_t)); 134 | parse_file("/proc/net/snmp", fields, NELEMS(fields), 1); 135 | } 136 | 137 | void get_tcpinfo(tcpinfo_t *tcpinfo) 138 | { 139 | field_t fields[] = { 140 | { "Tcp", 14, 141 | { &tcpinfo->tcpRtoAlgorithm, 142 | &tcpinfo->tcpRtoMin, 143 | &tcpinfo->tcpRtoMax, 144 | &tcpinfo->tcpMaxConn, 145 | &tcpinfo->tcpActiveOpens, 146 | &tcpinfo->tcpPassiveOpens, 147 | &tcpinfo->tcpAttemptFails, 148 | &tcpinfo->tcpEstabResets, 149 | &tcpinfo->tcpCurrEstab, 150 | &tcpinfo->tcpInSegs, 151 | &tcpinfo->tcpOutSegs, 152 | &tcpinfo->tcpRetransSegs, 153 | &tcpinfo->tcpInErrs, 154 | &tcpinfo->tcpOutRsts } }, 155 | }; 156 | 157 | if (parse_file("/proc/net/snmp", fields, NELEMS(fields), 1)) 158 | memset(tcpinfo, 0, sizeof(tcpinfo_t)); 159 | } 160 | 161 | void get_udpinfo(udpinfo_t *udpinfo) 162 | { 163 | field_t fields[] = { 164 | { "Udp", 4, 165 | { &udpinfo->udpInDatagrams, 166 | &udpinfo->udpNoPorts, 167 | &udpinfo->udpInErrors, 168 | &udpinfo->udpOutDatagrams } }, 169 | }; 170 | 171 | if (parse_file("/proc/net/snmp", fields, NELEMS(fields), 1)) 172 | memset(udpinfo, 0, sizeof(udpinfo_t)); 173 | } 174 | 175 | 176 | void get_diskinfo(diskinfo_t *diskinfo) 177 | { 178 | struct statfs fs; 179 | size_t i; 180 | 181 | memset(diskinfo, 0, sizeof(*diskinfo)); 182 | for (i = 0; i < g_disk_list_length; i++) { 183 | if (statfs(g_disk_list[i], &fs) == -1) 184 | continue; 185 | 186 | diskinfo->total[i] = ((float)fs.f_blocks * fs.f_bsize) / 1024; 187 | diskinfo->free[i] = ((float)fs.f_bfree * fs.f_bsize) / 1024; 188 | diskinfo->used[i] = ((float)(fs.f_blocks - fs.f_bfree) * fs.f_bsize) / 1024; 189 | diskinfo->blocks_used_percent[i] = 190 | ((float)(fs.f_blocks - fs.f_bfree) * 100 + fs.f_blocks - 1) / fs.f_blocks; 191 | if (fs.f_files <= 0) 192 | diskinfo->inodes_used_percent[i] = 0; 193 | else 194 | diskinfo->inodes_used_percent[i] = 195 | ((float)(fs.f_files - fs.f_ffree) * 100 + fs.f_files - 1) / fs.f_files; 196 | } 197 | } 198 | 199 | void get_netinfo(netinfo_t *netinfo) 200 | { 201 | struct ifaddrs *ifap, *ifa; 202 | field_t fields[MAX_NR_INTERFACES + 1]; 203 | 204 | if (getifaddrs(&ifap) < 0) 205 | return; 206 | 207 | memset(fields, 0, sizeof(fields)); 208 | memset(netinfo, 0, sizeof(*netinfo)); 209 | 210 | for (ifa = ifap; ifa; ifa = ifa->ifa_next) { 211 | struct sockaddr_in *addr, *mask, *bcaddr; 212 | struct sockaddr_ll *sll; 213 | int i; 214 | 215 | if (!ifa->ifa_addr) 216 | continue; 217 | 218 | i = find_ifname(ifa->ifa_name); 219 | if (i == -1) 220 | continue; 221 | 222 | switch (ifa->ifa_addr->sa_family) { 223 | case AF_INET: 224 | if (!ifa->ifa_addr || !ifa->ifa_netmask) 225 | continue; 226 | 227 | addr = (struct sockaddr_in *)ifa->ifa_addr; 228 | mask = (struct sockaddr_in *)ifa->ifa_netmask; 229 | if (addr) { 230 | netinfo->in_addr[i] = ntohl(addr->sin_addr.s_addr); 231 | netinfo->in_mask[i] = ntohl(mask->sin_addr.s_addr); 232 | } 233 | 234 | bcaddr = (struct sockaddr_in *)ifa->ifa_broadaddr; 235 | if (bcaddr && (ifa->ifa_flags & IFF_BROADCAST)) { 236 | netinfo->in_bcaddr[i] = ntohl(bcaddr->sin_addr.s_addr); 237 | netinfo->in_bcent[i] = netinfo->in_bcaddr[i] ? 1 : 0; 238 | } 239 | break; 240 | 241 | case AF_INET6: 242 | /* XXX: Not supported yet */ 243 | break; 244 | 245 | default: 246 | break; 247 | } 248 | 249 | if (!netinfo->stats[i]) { 250 | if (ifa->ifa_flags & IFF_POINTOPOINT) 251 | netinfo->if_type[i] = 23; /* ppp(23) */ 252 | else if (ifa->ifa_flags & IFF_LOOPBACK) 253 | netinfo->if_type[i] = 24; /* softwareLoopback(24) */ 254 | else 255 | netinfo->if_type[i] = 6; /* ethernetCsmacd(6) */ 256 | 257 | if (ifa->ifa_flags & IFF_UP) 258 | netinfo->status[i] = (ifa->ifa_flags & IFF_RUNNING) ? 1 : 7; 259 | else 260 | netinfo->status[i] = 2; 261 | 262 | sll = (struct sockaddr_ll *)ifa->ifa_addr; 263 | memcpy(netinfo->mac_addr[i], sll->sll_addr, sizeof(netinfo->mac_addr[i])); 264 | 265 | if (ethtool_gstats(i, netinfo, &fields[i]) < 0) { 266 | /* XXX: Tx multicast and Rx/Tx broadcast not available atm. */ 267 | fields[i].prefix = g_interface_list[i]; 268 | fields[i].len = 12; 269 | fields[i].value[0] = &netinfo->rx_bytes[i]; 270 | fields[i].value[1] = &netinfo->rx_packets[i]; 271 | fields[i].value[2] = &netinfo->rx_errors[i]; 272 | fields[i].value[3] = &netinfo->rx_drops[i]; 273 | fields[i].value[7] = &netinfo->rx_mc_packets[i]; 274 | fields[i].value[8] = &netinfo->tx_bytes[i]; 275 | fields[i].value[9] = &netinfo->tx_packets[i]; 276 | fields[i].value[10] = &netinfo->tx_errors[i]; 277 | fields[i].value[11] = &netinfo->tx_drops[i]; 278 | } 279 | 280 | if (-1 == read_file_value(&netinfo->if_mtu[i], "/sys/class/net/%s/mtu", g_interface_list[i])) 281 | netinfo->if_mtu[i] = 1500; /* Fallback */ 282 | 283 | if (-1 == read_file_value(&netinfo->if_speed[i], "/sys/class/net/%s/speed", g_interface_list[i])) 284 | netinfo->if_speed[i] = 1000; /* Fallback */ 285 | netinfo->if_speed[i] *= 1000000; /* to bps */ 286 | 287 | netinfo->ifindex[i] = if_nametoindex(ifa->ifa_name); 288 | 289 | /* XXX: Need better tracking on Linux, c.f. FreeBSD ... */ 290 | netinfo->lastchange[1] = get_process_uptime(); 291 | netinfo->stats[i] = 1; 292 | } 293 | } 294 | 295 | parse_file("/proc/net/dev", fields, NELEMS(fields), 0); 296 | freeifaddrs(ifap); 297 | } 298 | 299 | #endif /* __linux__ */ 300 | 301 | /* vim: ts=4 sts=4 sw=4 nowrap 302 | */ 303 | -------------------------------------------------------------------------------- /linux_ethtool.c: -------------------------------------------------------------------------------- 1 | /* Linux ethtool helpers 2 | * 3 | * Copyright (C) 2020 Bjørn Mork 4 | * 5 | * This file may be distributed and/or modified under the terms of the 6 | * GNU General Public License version 2 as published by the Free Software 7 | * Foundation and appearing in the file LICENSE.GPL included in the 8 | * packaging of this file. 9 | * 10 | * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE 11 | * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 12 | * 13 | * See COPYING for GPL licensing information. 14 | */ 15 | #ifdef __linux__ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include "ethtool-conf.h" 33 | #include "mini-snmpd.h" 34 | 35 | #ifdef CONFIG_ENABLE_ETHTOOL 36 | 37 | typedef unsigned long long u64; 38 | typedef uint32_t u32; 39 | typedef uint16_t u16; 40 | typedef uint8_t u8; 41 | typedef int32_t s32; 42 | 43 | /* counter offsets and number of counters per interface */ 44 | static struct ethtool_s { 45 | int n_stats; 46 | int rx_bytes; 47 | int rx_mc_packets; 48 | int rx_bc_packets; 49 | int rx_packets; 50 | int rx_errors; 51 | int rx_drops; 52 | int tx_bytes; 53 | int tx_mc_packets; 54 | int tx_bc_packets; 55 | int tx_packets; 56 | int tx_errors; 57 | int tx_drops; 58 | } ethtool[MAX_NR_INTERFACES]; 59 | 60 | /* ethtool socket */ 61 | static int fd = -1; 62 | 63 | static int ethtool_init() 64 | { 65 | fd = socket(AF_INET, SOCK_DGRAM, 0); 66 | if (fd < 0) 67 | fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); 68 | if (fd < 0) 69 | logit(LOG_ERR, errno, "Cannot get control socket"); 70 | return fd; 71 | } 72 | 73 | static struct ethtool_gstrings *get_stringset(const char *iname) 74 | { 75 | struct ifreq ifr = {}; 76 | struct { 77 | struct ethtool_sset_info hdr; 78 | u32 buf[1]; 79 | } sset_info; 80 | u32 len; 81 | struct ethtool_gstrings *strings; 82 | 83 | sset_info.hdr.cmd = ETHTOOL_GSSET_INFO; 84 | sset_info.hdr.reserved = 0; 85 | sset_info.hdr.sset_mask = 1ULL << ETH_SS_STATS; 86 | ifr.ifr_data = (void *)&sset_info; 87 | strcpy(ifr.ifr_name, iname); 88 | if (ioctl(fd, SIOCETHTOOL, &ifr) == 0) { 89 | const u32 *sset_lengths = sset_info.hdr.data; 90 | 91 | len = sset_info.hdr.sset_mask ? sset_lengths[0] : 0; 92 | } else { 93 | return NULL; 94 | } 95 | 96 | strings = calloc(1, sizeof(*strings) + len * ETH_GSTRING_LEN); 97 | if (!strings) 98 | return NULL; 99 | 100 | strings->cmd = ETHTOOL_GSTRINGS; 101 | strings->string_set = ETH_SS_STATS; 102 | strings->len = len; 103 | ifr.ifr_data = (void *)strings; 104 | if (len != 0 && ioctl(fd, SIOCETHTOOL, &ifr)) { 105 | free(strings); 106 | return NULL; 107 | } 108 | 109 | return strings; 110 | } 111 | 112 | static int ethtool_match_string(const char *key, struct ethtool_gstrings *strings) 113 | { 114 | unsigned int i; 115 | 116 | if (!key) 117 | return -1; 118 | for (i = 0; i < strings->len; i++) { 119 | if (!strncmp(key, (char *)&strings->data[i * ETH_GSTRING_LEN], ETH_GSTRING_LEN)) { 120 | logit(LOG_DEBUG, 0, "found '%s' match at index %u", key, i); 121 | return i; 122 | } 123 | } 124 | return -1; 125 | } 126 | 127 | #define ethtool_parse_opt(_name) \ 128 | ethtool[intf]._name = ethtool_match_string(cfg_getstr(cfg, #_name), strings); \ 129 | if (ethtool[intf]._name >= 0) \ 130 | found = 1; 131 | 132 | static void ethtool_xlate_intf(cfg_t *cfg, int intf, const char *iname) 133 | { 134 | struct ethtool_gstrings *strings = get_stringset(iname); 135 | int found = 0; 136 | 137 | if (!strings) 138 | return; 139 | 140 | logit(LOG_DEBUG, 0, "got ethtool stats strings for '%s'", iname); 141 | 142 | ethtool_parse_opt(rx_bytes); 143 | ethtool_parse_opt(rx_mc_packets); 144 | ethtool_parse_opt(rx_bc_packets); 145 | ethtool_parse_opt(rx_packets); 146 | ethtool_parse_opt(rx_errors); 147 | ethtool_parse_opt(rx_drops); 148 | ethtool_parse_opt(tx_bytes); 149 | ethtool_parse_opt(tx_mc_packets); 150 | ethtool_parse_opt(tx_bc_packets); 151 | ethtool_parse_opt(tx_packets); 152 | ethtool_parse_opt(tx_errors); 153 | ethtool_parse_opt(tx_drops); 154 | 155 | /* save the size of the stats table if we found at least one macth */ 156 | if (found) 157 | ethtool[intf].n_stats = strings->len; 158 | else 159 | logit(LOG_DEBUG, 0, "fount no matching string for '%s'", iname); 160 | 161 | free(strings); 162 | } 163 | 164 | void ethtool_xlate_cfg(cfg_t *cfg) 165 | { 166 | cfg_t *ethtool; 167 | const char *iname; 168 | unsigned int i, j; 169 | int intf; 170 | 171 | if (ethtool_init() < 0) 172 | return; 173 | 174 | for (i = 0; i < cfg_size(cfg, "ethtool"); i++) { 175 | ethtool = cfg_getnsec(cfg, "ethtool", i); 176 | iname = cfg_title(ethtool); 177 | logit(LOG_INFO, 0, "Parsing ethtool section '%s'", iname); 178 | 179 | /* exact match? */ 180 | intf = find_ifname((char *)iname); 181 | if (intf >= 0) { 182 | ethtool_xlate_intf(ethtool, intf, iname); 183 | continue; 184 | } 185 | 186 | /* or wildcard match? */ 187 | if (strcspn(iname, "*?[")) { 188 | for (j = 0; j < g_interface_list_length; j++) { 189 | if (!fnmatch(iname, g_interface_list[j], 0)) 190 | ethtool_xlate_intf(ethtool, j, g_interface_list[j]); 191 | } 192 | } 193 | } 194 | } 195 | 196 | #define set_val(_fieldnum, _name) \ 197 | if (ethtool[intf]._name >= 0 && ethtool[intf]._name < ethtool[intf].n_stats) \ 198 | netinfo->_name[intf] = stats->data[ethtool[intf]._name]; \ 199 | else if (_fieldnum >= 0) { \ 200 | fallback = 1; \ 201 | field->value[_fieldnum] = &netinfo->_name[intf]; \ 202 | } 203 | 204 | int ethtool_gstats(int intf, netinfo_t *netinfo, field_t *field) 205 | { 206 | struct ifreq ifr = {}; 207 | struct ethtool_stats *stats; 208 | unsigned int sz_stats; 209 | int fallback = 0; 210 | 211 | if (fd < 0) 212 | return fd; 213 | if (!ethtool[intf].n_stats) 214 | return -1; 215 | 216 | sz_stats = ethtool[intf].n_stats * sizeof(u64); 217 | stats = calloc(1, sz_stats + sizeof(struct ethtool_stats)); 218 | if (!stats) { 219 | logit(LOG_ERR, ENOMEM, "cannot allocate mem for ethtool stats"); 220 | return -ENOMEM; 221 | } 222 | 223 | stats->cmd = ETHTOOL_GSTATS; 224 | stats->n_stats = ethtool[intf].n_stats; 225 | strcpy(ifr.ifr_name, g_interface_list[intf]); 226 | ifr.ifr_data = (void *)stats; 227 | if (ioctl(fd, SIOCETHTOOL, &ifr) < 0) { 228 | logit(LOG_ERR, errno, "Cannot get ethtool stats"); 229 | free(stats); 230 | return -errno; 231 | } 232 | 233 | set_val( 0, rx_bytes); 234 | set_val( 7, rx_mc_packets); 235 | set_val(-1, rx_bc_packets); 236 | set_val( 1, rx_packets); 237 | set_val( 2, rx_errors); 238 | set_val( 3, rx_drops); 239 | set_val( 8, tx_bytes); 240 | set_val(-1, tx_mc_packets); 241 | set_val(-1, tx_bc_packets); 242 | set_val( 9, tx_packets); 243 | set_val(10, tx_errors); 244 | set_val(11, tx_drops); 245 | 246 | /* we can avoid parsing values from the dev file if there is no fallback counter */ 247 | if (fallback) { 248 | field->prefix = g_interface_list[intf]; 249 | field->len = 12; 250 | } 251 | free(stats); 252 | 253 | return 0; 254 | } 255 | 256 | #endif /* CONFIG_ENABLE_ETHTOOL */ 257 | #endif /* __linux__ */ 258 | 259 | /* vim: ts=4 sts=4 sw=4 nowrap 260 | */ 261 | -------------------------------------------------------------------------------- /mini-snmpd.8: -------------------------------------------------------------------------------- 1 | .Dd Feb 2, 2020 2 | .Dt MINI-SNMPD 8 SMM 3 | .Os 4 | .Sh NAME 5 | .Nm mini-snmpd 6 | .Nd a minimal implementation of an SNMP daemon 7 | .Sh SYNOPSIS 8 | .Nm mini-snmpd 9 | .Op Fl 4, -use-ipv4 10 | .Op Fl 6, -use-ipv6 11 | .Op Fl a, -auth 12 | .Op Fl c, -community Ar STR 13 | .Op Fl C, -contact Ar NAME 14 | .Op Fl d, -disks Ar DIR 15 | .Op Fl D, -description Ar STR 16 | .Op Fl f, -file Ar FILE 17 | .Op Fl h, -help 18 | .Op Fl i, -interfaces Ar IFNAME 19 | .Op Fl I, -listen Ar IFNAME 20 | .Op Fl l, -loglevel Ar LEVEL 21 | .Op Fl L, -location Ar STR 22 | .Op Fl n, -foreground 23 | .Op Fl p, -udp-port Ar PORT 24 | .Op Fl P, -tcp-port Ar PORT 25 | .Op Fl s, -syslog 26 | .Op Fl t, -timeout Ar SEC 27 | .Op Fl u, -drop-privs Ar USER 28 | .Op Fl v, -version 29 | .Op Fl V, -vendor Ar OID 30 | .Sh DESCRIPTION 31 | .Nm 32 | is a program that serves basic system parameters to clients using the 33 | Simple Network Management Protocol (SNMP) version 1 or 2c. 34 | .Pp 35 | By default 36 | .Nm 37 | starts in the background, detaching from the controlling terminal, like 38 | a traditional UNIX daemon. Modern init systems may require services to 39 | run in the foreground, to that end the 40 | .Fl n 41 | option in combination with the 42 | .Fl s 43 | option to enable logging using syslog. 44 | .Pp 45 | The default vendor OID is only a placeholder where you need to insert 46 | the PEN (private enterprise number) of your organization. If you do not 47 | have one you can get one assigned by the Internet Assigned Numbers 48 | Authority (IANA), free of charge. See their PEN application web page at 49 | .Xr http://pen.iana.org/ . 50 | .Sh OPTIONS 51 | .Bl -tag -width Ds 52 | .It Fl 4, -use-ipv4 53 | Use IPv4, default 54 | .It Fl 6, -use-ipv6 55 | Use IPv6 56 | .It Fl a, -auth 57 | Require client authentication, thus SNMP version 2c, default is off. 58 | .It Fl c, Fl -community Ar STR 59 | SNMP version 2c authentication, or community, string, default is 60 | "public". Remember to also enable 61 | .Fl -auth 62 | to activate authentication. 63 | .It Fl C, Fl -contact Ar NAME 64 | Contact address to device administrator, default is none. 65 | .It Fl d, Fl -disks Ar DIR[,DIR,DIR] 66 | The list of disk mount points to monitor, default is '/'. Separate 67 | multiple directories with a comma, colon, or a semicolon. 68 | .It Fl D, Fl -description Ar STR 69 | The description of the device, default is empty. 70 | .It Fl f, -file Ar FILE 71 | Configuration file, default: 72 | .Pa /etc/mini-snmpd.conf 73 | .It Fl h, -help 74 | Show summary of command line options and exit. 75 | .It Fl i, Fl -interface Ar IFNAME[,IFNAME] 76 | List of network interfaces to monitor for IF-MIB, default is none. 77 | Separate multiple interface names with comma or semicolon, 78 | .Em not 79 | colon! 80 | .It Fl I, Fl -listen Ar IFNAME 81 | Network interface to bind to, default is listen on all interfaces. 82 | .It Fl l, Fl -loglevel Ar LEVEL 83 | Set log level: none, err, info, notice, debug. Default: notice. 84 | .It Fl L, Fl -location Ar STR 85 | The location of the device, default is empty. 86 | .It Fl n, -foreground 87 | Run in foreground, do not detach from controlling terminal. 88 | .It Fl p, Fl -udp-port Ar PORT 89 | UDP port to listen to for incoming connections, default is 161. 90 | .It Fl P, Fl -tcp-port Ar PORT 91 | TCP port to listen to for incoming connections, default is 161. 92 | .It Fl s, -syslog 93 | Use syslog for logging, even if running in the foreground. 94 | .It Fl t, Fl -timeout Ar SEC 95 | Timeout for updating the MIB variables, default is 1 second. 96 | .It Fl u, -drop-privs Ar USER 97 | Drop privileges after opening sockets to 98 | .Ar USER , 99 | default: no. 100 | .It Fl v, Fl -version 101 | Show program version and exit. 102 | .It Fl V, Fl -vendor Ar OID 103 | The OID of the device vendor, this MUST be changed to your own 104 | organization's OID. Default is .1.3.6.1.4.1 105 | .El 106 | .Sh SIGNALS 107 | .Nm 108 | responds to the following signals: 109 | .Pp 110 | .Bl -tag -width TERM -compact 111 | .It TERM 112 | Tell 113 | .Nm 114 | to exit gracefully 115 | .It HUP 116 | Same as TERM 117 | .El 118 | .Sh FILES 119 | .Bl -tag -width /var/run/mini-snmpd.pid -compact 120 | .It Pa /etc/mini-snmpd.conf 121 | configuration file. See 122 | .Xr mini-snmpd.conf 5 123 | for more information. 124 | .It Pa /etc/default/mini-snmpd 125 | Sourced by systemd unit file, 126 | .Sy $DAEMON_OPTS 127 | is supported 128 | .It Pa /var/run/mini-snmpd.pid 129 | default process ID file 130 | .El 131 | .Sh SEE ALSO 132 | .Xr mini-snmpd.conf 5 133 | .Sh EXAMPLE 134 | mini-snmpd -a -c secret -D "My device" -d /cf -i lo,eth0 135 | .Pp 136 | This command starts the program at the default port, using the community 137 | string "secret", the system description "My device" and provides disk 138 | statistics for the "/cf" (compact flash) mount point, with network 139 | interface statistics for the interfaces "lo" and "eth0". 140 | .Sh AUTHORS 141 | .Nm 142 | was originally written by Robert Ernst 143 | .Aq mailto:robert.ernst@aon.at . 144 | It is currently maintained by Joachim Nilsson 145 | .Aq mailto:troglobit@gmail.com 146 | at GitHub. 147 | -------------------------------------------------------------------------------- /mini-snmpd.c: -------------------------------------------------------------------------------- 1 | /* Main program 2 | * 3 | * Copyright (C) 2008-2010 Robert Ernst 4 | * Copyright (C) 2015-2020 Joachim Nilsson 5 | * 6 | * This file may be distributed and/or modified under the terms of the 7 | * GNU General Public License version 2 as published by the Free Software 8 | * Foundation and appearing in the file LICENSE.GPL included in the 9 | * packaging of this file. 10 | * 11 | * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE 12 | * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 13 | * 14 | * See COPYING for GPL licensing information. 15 | */ 16 | 17 | #define _GNU_SOURCE 18 | 19 | #include 20 | #include /* MIN()/MAX() */ 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #define SYSLOG_NAMES /* Expose syslog.h:prioritynames[] */ 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | #include "mini-snmpd.h" 41 | 42 | static int usage(int rc) 43 | { 44 | printf("Usage: %s [options]\n" 45 | "\n" 46 | #ifdef CONFIG_ENABLE_IPV6 47 | " -4, --use-ipv4 Use IPv4, default\n" 48 | " -6, --use-ipv6 Use IPv6\n" 49 | #endif 50 | " -a, --auth Enable authentication, i.e. SNMP version 2c\n" 51 | " -c, --community STR Community string, default: public\n" 52 | " -C, --contact STR System contact, default: none\n" 53 | " -d, --disks PATH Disks to monitor, default: /\n" 54 | " -D, --description STR System description, default: none\n" 55 | #ifdef HAVE_LIBCONFUSE 56 | " -f, --file FILE Configuration file. Default: " SYSCONFDIR "/%s.conf\n" 57 | #endif 58 | " -h, --help This help text\n" 59 | " -i, --interfaces IFACE Network interfaces to monitor, default: none\n" 60 | " -I, --listen IFACE Network interface to listen, default: all\n" 61 | " -l, --loglevel LEVEL Set log level: none, err, info, notice*, debug\n" 62 | " -L, --location STR System location, default: none\n" 63 | " -n, --foreground Run in foreground, do not detach from controlling terminal\n" 64 | " -p, --udp-port PORT UDP port to bind to, default: 161\n" 65 | " -P, --tcp-port PORT TCP port to bind to, default: 161\n" 66 | " -s, --syslog Use syslog for logging, even if running in the foreground\n" 67 | " -t, --timeout SEC Timeout for MIB updates, default: 1 second\n" 68 | " -u, --drop-privs USER Drop privileges after opening sockets to USER, default: no\n" 69 | " -v, --version Show program version and exit\n" 70 | " -V, --vendor OID System vendor, default: none\n" 71 | "\n", g_prognm 72 | #ifdef HAVE_LIBCONFUSE 73 | , PACKAGE_NAME 74 | #endif 75 | ); 76 | printf("Bug report address: %s\n", PACKAGE_BUGREPORT); 77 | #ifdef PACKAGE_URL 78 | printf("Project homepage: %s\n", PACKAGE_URL); 79 | #endif 80 | 81 | return rc; 82 | } 83 | 84 | static void handle_signal(int UNUSED(signo)) 85 | { 86 | g_quit = 1; 87 | } 88 | 89 | static void handle_udp_client(void) 90 | { 91 | const char *req_msg = "Failed UDP request from"; 92 | const char *snd_msg = "Failed UDP response to"; 93 | my_sockaddr_t sockaddr; 94 | my_socklen_t socklen; 95 | ssize_t rv; 96 | char straddr[my_inet_addrstrlen] = { 0 }; 97 | 98 | memset(&sockaddr, 0, sizeof(sockaddr)); 99 | 100 | /* Read the whole UDP packet from the socket at once */ 101 | socklen = sizeof(sockaddr); 102 | rv = recvfrom(g_udp_sockfd, g_udp_client.packet, sizeof(g_udp_client.packet), 103 | 0, (struct sockaddr *)&sockaddr, &socklen); 104 | if (rv == -1) { 105 | logit(LOG_WARNING, errno, "Failed receiving UDP request on port %d", g_udp_port); 106 | return; 107 | } 108 | 109 | g_udp_client.timestamp = time(NULL); 110 | g_udp_client.sockfd = g_udp_sockfd; 111 | g_udp_client.addr = sockaddr.my_sin_addr; 112 | g_udp_client.port = sockaddr.my_sin_port; 113 | g_udp_client.size = rv; 114 | g_udp_client.outgoing = 0; 115 | #ifdef DEBUG 116 | dump_packet(&g_udp_client); 117 | #endif 118 | 119 | /* Call the protocol handler which will prepare the response packet */ 120 | inet_ntop(my_af_inet, &sockaddr.my_sin_addr, straddr, sizeof(straddr)); 121 | if (snmp(&g_udp_client) == -1) { 122 | logit(LOG_WARNING, errno, "%s %s:%d", req_msg, straddr, sockaddr.my_sin_port); 123 | return; 124 | } 125 | if (g_udp_client.size == 0) { 126 | logit(LOG_WARNING, 0, "%s %s:%d: ignored", req_msg, straddr, sockaddr.my_sin_port); 127 | return; 128 | } 129 | g_udp_client.outgoing = 1; 130 | 131 | /* Send the whole UDP packet to the socket at once */ 132 | rv = sendto(g_udp_sockfd, g_udp_client.packet, g_udp_client.size, 133 | MSG_DONTWAIT, (struct sockaddr *)&sockaddr, socklen); 134 | inet_ntop(my_af_inet, &sockaddr.my_sin_addr, straddr, sizeof(straddr)); 135 | if (rv == -1) 136 | logit(LOG_WARNING, errno, "%s %s:%d", snd_msg, straddr, sockaddr.my_sin_port); 137 | else if ((size_t)rv != g_udp_client.size) 138 | logit(LOG_WARNING, 0, "%s %s:%d: only %zd of %zu bytes sent", snd_msg, straddr, sockaddr.my_sin_port, rv, g_udp_client.size); 139 | 140 | #ifdef DEBUG 141 | dump_packet(&g_udp_client); 142 | #endif 143 | } 144 | 145 | static void handle_tcp_connect(void) 146 | { 147 | const char *msg = "Could not accept TCP connection"; 148 | my_sockaddr_t tmp_sockaddr; 149 | my_sockaddr_t sockaddr; 150 | my_socklen_t socklen; 151 | client_t *client; 152 | char straddr[my_inet_addrstrlen] = ""; 153 | int rv; 154 | 155 | memset(&tmp_sockaddr, 0, sizeof(tmp_sockaddr)); 156 | memset(&sockaddr, 0, sizeof(sockaddr)); 157 | 158 | /* Accept the new connection (remember the client's IP address and port) */ 159 | socklen = sizeof(sockaddr); 160 | rv = accept(g_tcp_sockfd, (struct sockaddr *)&sockaddr, &socklen); 161 | if (rv == -1) { 162 | logit(LOG_ERR, errno, "%s", msg); 163 | return; 164 | } 165 | if (rv >= FD_SETSIZE) { 166 | logit(LOG_ERR, 0, "%s: FD set overflow", msg); 167 | close(rv); 168 | return; 169 | } 170 | 171 | /* Create a new client control structure or overwrite the oldest one */ 172 | if (g_tcp_client_list_length >= MAX_NR_CLIENTS) { 173 | client = find_oldest_client(); 174 | if (!client) { 175 | logit(LOG_ERR, 0, "%s: internal error", msg); 176 | exit(EXIT_SYSCALL); 177 | } 178 | 179 | tmp_sockaddr.my_sin_addr = client->addr; 180 | tmp_sockaddr.my_sin_port = client->port; 181 | inet_ntop(my_af_inet, &tmp_sockaddr.my_sin_addr, straddr, sizeof(straddr)); 182 | logit(LOG_WARNING, 0, "Maximum number of %d clients reached, kicking out %s:%d", 183 | MAX_NR_CLIENTS, straddr, tmp_sockaddr.my_sin_port); 184 | close(client->sockfd); 185 | } else { 186 | client = allocate(sizeof(client_t)); 187 | if (!client) 188 | exit(EXIT_SYSCALL); 189 | 190 | g_tcp_client_list[g_tcp_client_list_length++] = client; 191 | } 192 | 193 | /* Now fill out the client control structure values */ 194 | inet_ntop(my_af_inet, &sockaddr.my_sin_addr, straddr, sizeof(straddr)); 195 | logit(LOG_DEBUG, 0, "Connected TCP client %s:%d", straddr, sockaddr.my_sin_port); 196 | client->timestamp = time(NULL); 197 | client->sockfd = rv; 198 | client->addr = sockaddr.my_sin_addr; 199 | client->port = sockaddr.my_sin_port; 200 | client->size = 0; 201 | client->outgoing = 0; 202 | } 203 | 204 | static void handle_tcp_client_write(client_t *client) 205 | { 206 | const char *msg = "Failed TCP response to"; 207 | ssize_t rv; 208 | char straddr[my_inet_addrstrlen] = ""; 209 | my_sockaddr_t sockaddr; 210 | 211 | /* Send the packet atomically and close socket if that did not work */ 212 | sockaddr.my_sin_addr = client->addr; 213 | sockaddr.my_sin_port = client->port; 214 | rv = send(client->sockfd, client->packet, client->size, 0); 215 | inet_ntop(my_af_inet, &sockaddr.my_sin_addr, straddr, sizeof(straddr)); 216 | if (rv == -1) { 217 | logit(LOG_WARNING, errno, "%s %s:%d", msg, straddr, sockaddr.my_sin_port); 218 | close(client->sockfd); 219 | client->sockfd = -1; 220 | return; 221 | } 222 | if ((size_t)rv != client->size) { 223 | logit(LOG_WARNING, 0, "%s %s:%d: only %zd of %zu bytes written", 224 | msg, straddr, sockaddr.my_sin_port, rv, client->size); 225 | close(client->sockfd); 226 | client->sockfd = -1; 227 | return; 228 | } 229 | 230 | #ifdef DEBUG 231 | dump_packet(client); 232 | #endif 233 | 234 | /* Put the client into listening mode again */ 235 | client->size = 0; 236 | client->outgoing = 0; 237 | } 238 | 239 | static void handle_tcp_client_read(client_t *client) 240 | { 241 | const char *req_msg = "Failed TCP request from"; 242 | int rv; 243 | char straddr[my_inet_addrstrlen] = ""; 244 | my_sockaddr_t sockaddr; 245 | 246 | /* Read from the socket what arrived and put it into the buffer */ 247 | sockaddr.my_sin_addr = client->addr; 248 | sockaddr.my_sin_port = client->port; 249 | rv = read(client->sockfd, client->packet + client->size, sizeof(client->packet) - client->size); 250 | inet_ntop(my_af_inet, &sockaddr.my_sin_addr, straddr, sizeof(straddr)); 251 | if (rv == -1) { 252 | logit(LOG_WARNING, errno, "%s %s:%d", req_msg, straddr, sockaddr.my_sin_port); 253 | close(client->sockfd); 254 | client->sockfd = -1; 255 | return; 256 | } 257 | if (rv == 0) { 258 | logit(LOG_DEBUG, 0, "TCP client %s:%d disconnected", 259 | straddr, sockaddr.my_sin_port); 260 | close(client->sockfd); 261 | client->sockfd = -1; 262 | return; 263 | } 264 | client->timestamp = time(NULL); 265 | client->size += rv; 266 | 267 | /* Check whether the packet was fully received and handle packet if yes */ 268 | rv = snmp_packet_complete(client); 269 | if (rv == -1) { 270 | logit(LOG_WARNING, errno, "%s %s:%d", req_msg, straddr, sockaddr.my_sin_port); 271 | close(client->sockfd); 272 | client->sockfd = -1; 273 | return; 274 | } 275 | if (rv == 0) { 276 | return; 277 | } 278 | client->outgoing = 0; 279 | 280 | #ifdef DEBUG 281 | dump_packet(client); 282 | #endif 283 | 284 | /* Call the protocol handler which will prepare the response packet */ 285 | if (snmp(client) == -1) { 286 | logit(LOG_WARNING, errno, "%s %s:%d", req_msg, straddr, sockaddr.my_sin_port); 287 | close(client->sockfd); 288 | client->sockfd = -1; 289 | return; 290 | } 291 | if (client->size == 0) { 292 | logit(LOG_WARNING, 0, "%s %s:%d: ignored", req_msg, straddr, sockaddr.my_sin_port); 293 | close(client->sockfd); 294 | client->sockfd = -1; 295 | return; 296 | } 297 | 298 | client->outgoing = 1; 299 | } 300 | 301 | static int log_level(char *arg) 302 | { 303 | int i, rc; 304 | 305 | for (i = 0; prioritynames[i].c_name; i++) { 306 | size_t min = MIN(strlen(prioritynames[i].c_name), strlen(arg)); 307 | 308 | if (!strncasecmp(prioritynames[i].c_name, arg, min)) { 309 | g_level = prioritynames[i].c_val; 310 | return 0; 311 | } 312 | } 313 | 314 | rc = atoi(arg); 315 | if (-1 == rc) 316 | return rc; 317 | 318 | g_level = rc; 319 | return 0; 320 | } 321 | 322 | static char *progname(char *arg0) 323 | { 324 | char *nm; 325 | 326 | nm = strrchr(arg0, '/'); 327 | if (nm) 328 | nm++; 329 | else 330 | nm = arg0; 331 | 332 | return nm; 333 | } 334 | 335 | int main(int argc, char *argv[]) 336 | { 337 | static const char short_options[] = "ac:C:d:D:hi:l:L:np:P:st:u:vV:" 338 | #ifndef __FreeBSD__ 339 | "I:" 340 | #endif 341 | #ifdef CONFIG_ENABLE_IPV6 342 | "46" 343 | #endif 344 | #ifdef HAVE_LIBCONFUSE 345 | "f:" 346 | #endif 347 | ; 348 | static const struct option long_options[] = { 349 | #ifdef CONFIG_ENABLE_IPV6 350 | { "use-ipv4", 0, 0, '4' }, 351 | { "use-ipv6", 0, 0, '6' }, 352 | #endif 353 | { "auth", 0, 0, 'a' }, 354 | { "community", 1, 0, 'c' }, 355 | { "contact", 1, 0, 'C' }, 356 | { "disks", 1, 0, 'd' }, 357 | { "description", 1, 0, 'D' }, 358 | #ifdef HAVE_LIBCONFUSE 359 | { "file", 1, 0, 'f' }, 360 | #endif 361 | { "help", 0, 0, 'h' }, 362 | { "interfaces", 1, 0, 'i' }, 363 | #ifndef __FreeBSD__ 364 | { "listen", 1, 0, 'I' }, 365 | #endif 366 | { "loglevel", 1, 0, 'l' }, 367 | { "location", 1, 0, 'L' }, 368 | { "foreground", 0, 0, 'n' }, 369 | { "udp-port", 1, 0, 'p' }, 370 | { "tcp-port", 1, 0, 'P' }, 371 | { "syslog", 0, 0, 's' }, 372 | { "timeout", 1, 0, 't' }, 373 | { "drop-privs", 1, 0, 'u' }, 374 | { "version", 0, 0, 'v' }, 375 | { "vendor", 1, 0, 'V' }, 376 | { NULL, 0, 0, 0 } 377 | }; 378 | int ticks, nfds, c, option_index = 1; 379 | size_t i; 380 | fd_set rfds, wfds; 381 | struct sigaction sig; 382 | #ifndef __FreeBSD__ 383 | struct ifreq ifreq; 384 | #endif 385 | struct timeval tv_last; 386 | struct timeval tv_now; 387 | struct timeval tv_sleep; 388 | my_socklen_t socklen; 389 | union { 390 | struct sockaddr_in sa; 391 | #ifdef CONFIG_ENABLE_IPV6 392 | struct sockaddr_in6 sa6; 393 | #endif 394 | } sockaddr; 395 | #ifdef HAVE_LIBCONFUSE 396 | char path[256] = ""; 397 | char *config = NULL; 398 | #endif 399 | 400 | g_prognm = progname(argv[0]); 401 | 402 | /* Parse commandline options */ 403 | while (1) { 404 | c = getopt_long(argc, argv, short_options, long_options, &option_index); 405 | if (c == -1) 406 | break; 407 | 408 | switch (c) { 409 | #ifdef CONFIG_ENABLE_IPV6 410 | case '4': 411 | g_family = AF_INET; 412 | break; 413 | 414 | case '6': 415 | g_family = AF_INET6; 416 | break; 417 | #endif 418 | case 'a': 419 | g_auth = 1; 420 | break; 421 | 422 | case 'c': 423 | g_community = optarg; 424 | break; 425 | 426 | case 'C': 427 | g_contact = optarg; 428 | break; 429 | 430 | case 'd': 431 | g_disk_list_length = split(optarg, ",:;", g_disk_list, MAX_NR_DISKS); 432 | break; 433 | 434 | case 'D': 435 | g_description = optarg; 436 | break; 437 | #ifdef HAVE_LIBCONFUSE 438 | case 'f': 439 | config = optarg; 440 | break; 441 | #endif 442 | case 'h': 443 | return usage(0); 444 | 445 | case 'i': 446 | g_interface_list_length = split(optarg, ",;", g_interface_list, MAX_NR_INTERFACES); 447 | break; 448 | #ifndef __FreeBSD__ 449 | case 'I': 450 | g_bind_to_device = strdup(optarg); 451 | break; 452 | #endif 453 | case 'l': 454 | if (log_level(optarg)) 455 | return usage(1); 456 | break; 457 | 458 | case 'L': 459 | g_location = optarg; 460 | break; 461 | 462 | case 'n': 463 | g_daemon = 0; 464 | break; 465 | 466 | case 'p': 467 | g_udp_port = atoi(optarg); 468 | break; 469 | 470 | case 'P': 471 | g_tcp_port = atoi(optarg); 472 | break; 473 | 474 | case 's': 475 | g_syslog = 1; 476 | break; 477 | 478 | case 't': 479 | g_timeout = atoi(optarg); 480 | break; 481 | 482 | case 'u': 483 | g_user = optarg; 484 | break; 485 | 486 | case 'v': 487 | printf("v" PACKAGE_VERSION "\n"); 488 | return 0; 489 | 490 | case 'V': 491 | g_vendor = optarg; 492 | break; 493 | 494 | default: 495 | return usage(EXIT_ARGS); 496 | } 497 | } 498 | 499 | if (g_syslog) 500 | openlog(g_prognm, LOG_CONS | LOG_PID, LOG_DAEMON); 501 | 502 | logit(LOG_NOTICE, 0, PROGRAM_IDENT " starting"); 503 | 504 | if (g_daemon) { 505 | logit(LOG_DEBUG, 0, "Daemonizing ..."); 506 | if (-1 == daemon(0, 0)) { 507 | logit(LOG_ERR, errno, "Failed daemonizing"); 508 | return 1; 509 | } 510 | } 511 | 512 | #ifdef HAVE_LIBCONFUSE 513 | if (!config) { 514 | snprintf(path, sizeof(path), "%s/%s.conf", SYSCONFDIR, PACKAGE_NAME); 515 | config = path; 516 | } else if (access(config, F_OK)) { 517 | logit(LOG_ERR, errno, "Failed reading config file '%s'", config); 518 | return 1; 519 | } 520 | 521 | if (read_config(config)) 522 | return 1; 523 | #endif 524 | 525 | if (!g_community) 526 | g_community = "public"; 527 | if (!g_vendor) 528 | g_vendor = VENDOR; 529 | if (!g_description) 530 | g_description = ""; 531 | if (!g_location) 532 | g_location = ""; 533 | if (!g_contact) 534 | g_contact = ""; 535 | 536 | g_timeout *= 100; 537 | 538 | /* Store the starting time since we need it for MIB updates */ 539 | if (gettimeofday(&tv_last, NULL) == -1) { 540 | memset(&tv_last, 0, sizeof(tv_last)); 541 | memset(&tv_sleep, 0, sizeof(tv_sleep)); 542 | } else { 543 | tv_sleep.tv_sec = g_timeout / 100; 544 | tv_sleep.tv_usec = (g_timeout % 100) * 10000; 545 | } 546 | 547 | /* Build the MIB and execute the first MIB update to get actual values */ 548 | if (mib_build() == -1) 549 | exit(EXIT_SYSCALL); 550 | if (mib_update(1) == -1) 551 | exit(EXIT_SYSCALL); 552 | 553 | /* Prevent TERM and HUP signals from interrupting system calls */ 554 | sig.sa_handler = handle_signal; 555 | sigemptyset (&sig.sa_mask); 556 | sig.sa_flags = SA_RESTART; 557 | sigaction(SIGTERM, &sig, NULL); 558 | sigaction(SIGINT, &sig, NULL); 559 | sigaction(SIGHUP, &sig, NULL); 560 | 561 | #ifdef DEBUG 562 | dump_mib(g_mib, g_mib_length); 563 | #endif 564 | 565 | /* Open the server's UDP port and prepare it for listening */ 566 | g_udp_sockfd = socket((g_family == AF_INET) ? PF_INET : PF_INET6, SOCK_DGRAM, 0); 567 | if (g_udp_sockfd == -1) { 568 | logit(LOG_ERR, errno, "could not create UDP socket"); 569 | exit(EXIT_SYSCALL); 570 | } 571 | 572 | if (g_family == AF_INET) { 573 | sockaddr.sa.sin_family = g_family; 574 | sockaddr.sa.sin_port = htons(g_udp_port); 575 | sockaddr.sa.sin_addr = inaddr_any; 576 | socklen = sizeof(sockaddr.sa); 577 | #ifdef CONFIG_ENABLE_IPV6 578 | } else { 579 | sockaddr.sa6.sin6_family = g_family; 580 | sockaddr.sa6.sin6_port = htons(g_udp_port); 581 | sockaddr.sa6.sin6_addr = in6addr_any; 582 | socklen = sizeof(sockaddr.sa6); 583 | #endif 584 | } 585 | if (bind(g_udp_sockfd, (struct sockaddr *)&sockaddr, socklen) == -1) { 586 | logit(LOG_ERR, errno, "could not bind UDP socket to port %d", g_udp_port); 587 | exit(EXIT_SYSCALL); 588 | } 589 | 590 | #ifndef __FreeBSD__ 591 | if (g_bind_to_device) { 592 | snprintf(ifreq.ifr_ifrn.ifrn_name, sizeof(ifreq.ifr_ifrn.ifrn_name), "%s", g_bind_to_device); 593 | if (setsockopt(g_udp_sockfd, SOL_SOCKET, SO_BINDTODEVICE, (char *)&ifreq, sizeof(ifreq)) == -1) { 594 | logit(LOG_WARNING, errno, "could not bind UDP socket to device %s", g_bind_to_device); 595 | exit(EXIT_SYSCALL); 596 | } 597 | } 598 | #endif 599 | 600 | /* Open the server's TCP port and prepare it for listening */ 601 | g_tcp_sockfd = socket((g_family == AF_INET) ? PF_INET : PF_INET6, SOCK_STREAM, 0); 602 | if (g_tcp_sockfd == -1) { 603 | logit(LOG_ERR, errno, "could not create TCP socket"); 604 | exit(EXIT_SYSCALL); 605 | } 606 | 607 | #ifndef __FreeBSD__ 608 | if (g_bind_to_device) { 609 | snprintf(ifreq.ifr_ifrn.ifrn_name, sizeof(ifreq.ifr_ifrn.ifrn_name), "%s", g_bind_to_device); 610 | if (setsockopt(g_tcp_sockfd, SOL_SOCKET, SO_BINDTODEVICE, (char *)&ifreq, sizeof(ifreq)) == -1) { 611 | logit(LOG_WARNING, errno, "could not bind TCP socket to device %s", g_bind_to_device); 612 | exit(EXIT_SYSCALL); 613 | } 614 | } 615 | #endif 616 | 617 | c = 1; 618 | if (setsockopt(g_tcp_sockfd, SOL_SOCKET, SO_REUSEADDR, &c, sizeof(c)) == -1) { 619 | logit(LOG_WARNING, errno, "could not set SO_REUSEADDR on TCP socket"); 620 | exit(EXIT_SYSCALL); 621 | } 622 | 623 | if (g_family == AF_INET) { 624 | sockaddr.sa.sin_family = g_family; 625 | sockaddr.sa.sin_port = htons(g_udp_port); 626 | sockaddr.sa.sin_addr = inaddr_any; 627 | socklen = sizeof(sockaddr.sa); 628 | #ifdef CONFIG_ENABLE_IPV6 629 | } else { 630 | sockaddr.sa6.sin6_family = g_family; 631 | sockaddr.sa6.sin6_port = htons(g_udp_port); 632 | sockaddr.sa6.sin6_addr = in6addr_any; 633 | socklen = sizeof(sockaddr.sa6); 634 | #endif 635 | } 636 | if (bind(g_tcp_sockfd, (struct sockaddr *)&sockaddr, socklen) == -1) { 637 | logit(LOG_ERR, errno, "could not bind TCP socket to port %d", g_tcp_port); 638 | exit(EXIT_SYSCALL); 639 | } 640 | 641 | if (listen(g_tcp_sockfd, 128) == -1) { 642 | logit(LOG_ERR, errno, "could not prepare TCP socket for listening"); 643 | exit(EXIT_SYSCALL); 644 | } 645 | 646 | /* Print a starting message (so the user knows the args were ok) */ 647 | if (g_bind_to_device) 648 | logit(LOG_NOTICE, 0, "Listening on port %d/udp and %d/tcp on interface %s", 649 | g_udp_port, g_tcp_port, g_bind_to_device); 650 | else 651 | logit(LOG_NOTICE, 0, "Listening on port %d/udp and %d/tcp", g_udp_port, g_tcp_port); 652 | 653 | if (g_user && geteuid() == 0) { 654 | struct passwd *pwd; 655 | struct group *grp; 656 | 657 | errno = 0; 658 | 659 | pwd = getpwnam(g_user); 660 | if (pwd == NULL) { 661 | logit(LOG_ERR, errno, "Unable to get UID for user \"%s\"", g_user); 662 | exit(EXIT_SYSCALL); 663 | } 664 | 665 | errno = 0; 666 | 667 | grp = getgrnam(g_user); 668 | if (grp == NULL) { 669 | logit(LOG_ERR, errno, "Unable to get GID for group \"%s\"", g_user); 670 | exit(EXIT_SYSCALL); 671 | } 672 | 673 | if (setgid(grp->gr_gid) == -1) { 674 | logit(LOG_ERR, errno, "Unable to set new group \"%s\"", g_user); 675 | exit(EXIT_SYSCALL); 676 | } 677 | 678 | if (setuid(pwd->pw_uid) == -1) { 679 | logit(LOG_ERR, errno, "Unable to set new user \"%s\"", g_user); 680 | exit(EXIT_SYSCALL); 681 | } 682 | 683 | logit(LOG_INFO, 0, "Successfully dropped privileges to %s:%s", g_user, g_user); 684 | } 685 | 686 | /* 687 | * Tell system we're up and running by creating /run/mini-snmpd.pid 688 | */ 689 | if (pidfile(NULL)) 690 | logit(LOG_ERR, errno, "Failed creating PID file"); 691 | 692 | /* Handle incoming connect requests and incoming data */ 693 | while (!g_quit) { 694 | /* Sleep until we get a request or the timeout is over */ 695 | FD_ZERO(&rfds); 696 | FD_ZERO(&wfds); 697 | FD_SET(g_udp_sockfd, &rfds); 698 | FD_SET(g_tcp_sockfd, &rfds); 699 | nfds = (g_udp_sockfd > g_tcp_sockfd) ? g_udp_sockfd : g_tcp_sockfd; 700 | 701 | for (i = 0; i < g_tcp_client_list_length; i++) { 702 | if (g_tcp_client_list[i]->outgoing) 703 | FD_SET(g_tcp_client_list[i]->sockfd, &wfds); 704 | else 705 | FD_SET(g_tcp_client_list[i]->sockfd, &rfds); 706 | 707 | if (nfds < g_tcp_client_list[i]->sockfd) 708 | nfds = g_tcp_client_list[i]->sockfd; 709 | } 710 | 711 | if (select(nfds + 1, &rfds, &wfds, NULL, &tv_sleep) == -1) { 712 | if (g_quit) 713 | break; 714 | 715 | logit(LOG_ERR, errno, "could not select from sockets"); 716 | exit(EXIT_SYSCALL); 717 | } 718 | 719 | /* Determine whether to update the MIB and the next ticks to sleep */ 720 | ticks = ticks_since(&tv_last, &tv_now); 721 | if (ticks < 0 || ticks >= g_timeout) { 722 | logit(LOG_DEBUG, 0, "updating the MIB (full)"); 723 | if (mib_update(1) == -1) 724 | exit(EXIT_SYSCALL); 725 | 726 | memcpy(&tv_last, &tv_now, sizeof(tv_now)); 727 | tv_sleep.tv_sec = g_timeout / 100; 728 | tv_sleep.tv_usec = (g_timeout % 100) * 10000; 729 | } else { 730 | logit(LOG_DEBUG, 0, "updating the MIB (partial)"); 731 | if (mib_update(0) == -1) 732 | exit(EXIT_SYSCALL); 733 | 734 | tv_sleep.tv_sec = (g_timeout - ticks) / 100; 735 | tv_sleep.tv_usec = ((g_timeout - ticks) % 100) * 10000; 736 | } 737 | 738 | #ifdef DEBUG 739 | dump_mib(g_mib, g_mib_length); 740 | #endif 741 | 742 | /* Handle UDP packets, TCP packets and TCP connection connects */ 743 | if (FD_ISSET(g_udp_sockfd, &rfds)) 744 | handle_udp_client(); 745 | 746 | if (FD_ISSET(g_tcp_sockfd, &rfds)) 747 | handle_tcp_connect(); 748 | 749 | for (i = 0; i < g_tcp_client_list_length; i++) { 750 | if (g_tcp_client_list[i]->outgoing) { 751 | if (FD_ISSET(g_tcp_client_list[i]->sockfd, &wfds)) 752 | handle_tcp_client_write(g_tcp_client_list[i]); 753 | } else { 754 | if (FD_ISSET(g_tcp_client_list[i]->sockfd, &rfds)) 755 | handle_tcp_client_read(g_tcp_client_list[i]); 756 | } 757 | } 758 | 759 | /* If there was a TCP disconnect, remove the client from the list */ 760 | for (i = 0; i < g_tcp_client_list_length; i++) { 761 | if (g_tcp_client_list[i]->sockfd == -1) { 762 | g_tcp_client_list_length--; 763 | if (i < g_tcp_client_list_length) { 764 | size_t len = (g_tcp_client_list_length - i) * sizeof(g_tcp_client_list[i]); 765 | 766 | free(g_tcp_client_list[i]); 767 | memmove(&g_tcp_client_list[i], &g_tcp_client_list[i + 1], len); 768 | 769 | /* 770 | * list changed, there could be more than 771 | * one to remove, start from begining 772 | */ 773 | i = -1; 774 | } 775 | } 776 | } 777 | } 778 | 779 | /* We were signaled, print a message and exit */ 780 | logit(LOG_NOTICE, 0, PROGRAM_IDENT " stopping"); 781 | if (g_syslog) 782 | closelog(); 783 | 784 | return EXIT_OK; 785 | } 786 | 787 | /* vim: ts=4 sts=4 sw=4 nowrap 788 | */ 789 | -------------------------------------------------------------------------------- /mini-snmpd.conf: -------------------------------------------------------------------------------- 1 | # mini-snmpd.conf: Example 2 | 3 | location = "Exampleville Site 1." 4 | contact = "ops@example.com" 5 | description = "NAS monitor" 6 | 7 | # Vendor OID tree 8 | vendor = ".1.3.6.1.4.1" 9 | 10 | # true/false, or yes/no 11 | authentication = true 12 | community = "public" 13 | 14 | # MIB poll timeout, sec 15 | timeout = 1 16 | 17 | # Disks to monitor, i.e. mount points in UCD-SNMP-MIB::dskTable 18 | disk-table = { "/", } 19 | 20 | # Interfaces to monitor, currently only for IF-MIB::ifTable 21 | #iface-table = { "eth0", "eth1" } 22 | 23 | # Use ethtool statistics 24 | #ethtool "eth*" { 25 | # rx_bytes = ifInOctets 26 | # rx_mc_packets = ifInMulticastPkts 27 | # rx_bc_packets = ifInBroadcastPkts 28 | # rx_packets = ifInUcastPkts 29 | # rx_errors = Jabbers 30 | # rx_drops = dot1dTpPortInDiscards 31 | # tx_bytes = ifOutOctets 32 | # tx_mc_packets = ifOutMulticastPkts 33 | # tx_bc_packets = ifOutBroadcastPkts 34 | # tx_packets = ifOutUcastPkts 35 | # tx_errors = Collisions 36 | # tx_drops = ifOutDiscards 37 | #} 38 | -------------------------------------------------------------------------------- /mini-snmpd.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2008-2010 Robert Ernst 3 | * Copyright (C) 2015-2020 Joachim Nilsson 4 | * 5 | * This file may be distributed and/or modified under the terms of the 6 | * GNU General Public License version 2 as published by the Free Software 7 | * Foundation and appearing in the file LICENSE.GPL included in the 8 | * packaging of this file. 9 | * 10 | * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE 11 | * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 12 | * 13 | * See COPYING for GPL licensing information. 14 | */ 15 | 16 | #ifndef MINI_SNMPD_H_ 17 | #define MINI_SNMPD_H_ 18 | 19 | #include "config.h" 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include "compat.h" 28 | 29 | /* 30 | * Project dependent defines 31 | */ 32 | 33 | #define EXIT_OK 0 34 | #define EXIT_ARGS 1 35 | #define EXIT_SYSCALL 2 36 | 37 | #define MAX_NR_CLIENTS 16 38 | #define MAX_NR_OIDS 20 39 | #define MAX_NR_SUBIDS 20 40 | #define MAX_NR_DISKS 4 41 | #define MAX_NR_INTERFACES 8 42 | #define MAX_NR_VALUES 2048 43 | 44 | #define MAX_PACKET_SIZE 2048 45 | #define MAX_STRING_SIZE 64 46 | 47 | /* 48 | * SNMP dependent defines 49 | */ 50 | 51 | #define BER_TYPE_BOOLEAN 0x01 52 | #define BER_TYPE_INTEGER 0x02 53 | #define BER_TYPE_BIT_STRING 0x03 54 | #define BER_TYPE_OCTET_STRING 0x04 55 | #define BER_TYPE_NULL 0x05 56 | #define BER_TYPE_OID 0x06 57 | #define BER_TYPE_SEQUENCE 0x30 58 | #define BER_TYPE_IP_ADDRESS 0x40 59 | #define BER_TYPE_COUNTER 0x41 60 | #define BER_TYPE_GAUGE 0x42 61 | #define BER_TYPE_TIME_TICKS 0x43 62 | #define BER_TYPE_COUNTER64 0x46 63 | #define BER_TYPE_NO_SUCH_OBJECT 0x80 64 | #define BER_TYPE_NO_SUCH_INSTANCE 0x81 65 | #define BER_TYPE_END_OF_MIB_VIEW 0x82 66 | #define BER_TYPE_SNMP_GET 0xA0 67 | #define BER_TYPE_SNMP_GETNEXT 0xA1 68 | #define BER_TYPE_SNMP_RESPONSE 0xA2 69 | #define BER_TYPE_SNMP_SET 0xA3 70 | #define BER_TYPE_SNMP_GETBULK 0xA5 71 | #define BER_TYPE_SNMP_INFORM 0xA6 72 | #define BER_TYPE_SNMP_TRAP 0xA7 73 | #define BER_TYPE_SNMP_REPORT 0xA8 74 | 75 | #define SNMP_VERSION_1 0 76 | #define SNMP_VERSION_2C 1 77 | #define SNMP_VERSION_3 3 78 | 79 | #define SNMP_STATUS_OK 0 80 | #define SNMP_STATUS_TOO_BIG 1 81 | #define SNMP_STATUS_NO_SUCH_NAME 2 82 | #define SNMP_STATUS_BAD_VALUE 3 83 | #define SNMP_STATUS_READ_ONLY 4 84 | #define SNMP_STATUS_GEN_ERR 5 85 | #define SNMP_STATUS_NO_ACCESS 6 86 | #define SNMP_STATUS_WRONG_TYPE 7 87 | #define SNMP_STATUS_WRONG_LENGTH 8 88 | #define SNMP_STATUS_WRONG_ENCODING 9 89 | #define SNMP_STATUS_WRONG_VALUE 10 90 | #define SNMP_STATUS_NO_CREATION 11 91 | #define SNMP_STATUS_INCONSISTENT_VALUE 12 92 | #define SNMP_STATUS_RESOURCE_UNAVAILABLE 13 93 | #define SNMP_STATUS_COMMIT_FAILED 14 94 | #define SNMP_STATUS_UNDO_FAILED 15 95 | #define SNMP_STATUS_AUTHORIZATION_ERROR 16 96 | #define SNMP_STATUS_NOT_WRITABLE 17 97 | #define SNMP_STATUS_INCONSISTENT_NAME 18 98 | 99 | #define PROGRAM_IDENT PACKAGE_NAME " v" PACKAGE_VERSION 100 | 101 | #ifndef CONFIG_ENABLE_IPV6 102 | #define my_sockaddr_t struct sockaddr_in 103 | #define my_socklen_t socklen_t 104 | #define my_sin_addr sin_addr 105 | #define my_sin_port sin_port 106 | #define my_sin_family sin_family 107 | #define my_af_inet AF_INET 108 | #define my_pf_inet PF_INET 109 | #define my_in_addr_t struct in_addr 110 | #define my_in_port_t in_port_t 111 | #define my_inet_addrstrlen INET_ADDRSTRLEN 112 | 113 | #else /* IPv6 */ 114 | 115 | #define my_sockaddr_t struct sockaddr_in6 116 | #define my_socklen_t socklen_t 117 | #define my_sin_addr sin6_addr 118 | #define my_sin_port sin6_port 119 | #define my_sin_family sin6_family 120 | #define my_af_inet AF_INET6 121 | #define my_pf_inet PF_INET6 122 | #define my_in_addr_t struct in6_addr 123 | #define my_in_port_t in_port_t 124 | #define my_inet_addrstrlen INET6_ADDRSTRLEN 125 | #endif/* CONFIG_ENABLE_IPV6 */ 126 | 127 | 128 | /* 129 | * Data types 130 | */ 131 | 132 | typedef struct client_s { 133 | time_t timestamp; 134 | int sockfd; 135 | my_in_addr_t addr; 136 | my_in_port_t port; 137 | unsigned char packet[MAX_PACKET_SIZE]; 138 | size_t size; 139 | int outgoing; 140 | } client_t; 141 | 142 | typedef struct oid_s { 143 | unsigned int subid_list[MAX_NR_SUBIDS]; 144 | size_t subid_list_length; 145 | short encoded_length; 146 | } oid_t; 147 | 148 | typedef struct data_s { 149 | unsigned char *buffer; 150 | size_t max_length; 151 | short encoded_length; 152 | } data_t; 153 | 154 | typedef struct value_s { 155 | oid_t oid; 156 | data_t data; 157 | } value_t; 158 | 159 | typedef struct field_s { 160 | char *prefix; 161 | 162 | size_t len; 163 | long long *value[24]; 164 | } field_t; 165 | 166 | typedef struct request_s { 167 | char community[MAX_STRING_SIZE]; 168 | int type; 169 | int version; 170 | int id; 171 | uint32_t non_repeaters; 172 | uint32_t max_repetitions; 173 | oid_t oid_list[MAX_NR_OIDS]; 174 | size_t oid_list_length; 175 | } request_t; 176 | 177 | typedef struct response_s { 178 | int error_status; 179 | int error_index; 180 | value_t value_list[MAX_NR_VALUES]; 181 | size_t value_list_length; 182 | } response_t; 183 | 184 | typedef struct loadinfo_s { 185 | unsigned int avg[3]; 186 | } loadinfo_t; 187 | 188 | typedef struct meminfo_s { 189 | long long total; 190 | long long free; 191 | long long shared; 192 | long long buffers; 193 | long long cached; 194 | } meminfo_t; 195 | 196 | typedef struct cpuinfo_s { 197 | long long user; 198 | long long nice; 199 | long long system; 200 | long long idle; 201 | long long irqs; 202 | long long cntxts; 203 | } cpuinfo_t; 204 | 205 | typedef struct diskinfo_s { 206 | unsigned int total[MAX_NR_DISKS]; 207 | unsigned int free[MAX_NR_DISKS]; 208 | unsigned int used[MAX_NR_DISKS]; 209 | unsigned int blocks_used_percent[MAX_NR_DISKS]; 210 | unsigned int inodes_used_percent[MAX_NR_DISKS]; 211 | } diskinfo_t; 212 | 213 | typedef struct netinfo_s { 214 | unsigned int in_addr[MAX_NR_INTERFACES]; 215 | unsigned int in_mask[MAX_NR_INTERFACES]; 216 | unsigned int in_bcaddr[MAX_NR_INTERFACES]; 217 | unsigned int in_bcent[MAX_NR_INTERFACES]; 218 | unsigned int if_type[MAX_NR_INTERFACES]; 219 | unsigned int if_mtu[MAX_NR_INTERFACES]; 220 | unsigned int if_speed[MAX_NR_INTERFACES]; 221 | unsigned int ifindex[MAX_NR_INTERFACES]; 222 | unsigned int status[MAX_NR_INTERFACES]; 223 | unsigned int lastchange[MAX_NR_INTERFACES]; 224 | unsigned int stats[MAX_NR_INTERFACES]; /* Sentinel for backends */ 225 | long long rx_bytes[MAX_NR_INTERFACES]; 226 | long long rx_mc_packets[MAX_NR_INTERFACES]; 227 | long long rx_bc_packets[MAX_NR_INTERFACES]; 228 | long long rx_packets[MAX_NR_INTERFACES]; 229 | long long rx_errors[MAX_NR_INTERFACES]; 230 | long long rx_drops[MAX_NR_INTERFACES]; 231 | long long tx_bytes[MAX_NR_INTERFACES]; 232 | long long tx_mc_packets[MAX_NR_INTERFACES]; 233 | long long tx_bc_packets[MAX_NR_INTERFACES]; 234 | long long tx_packets[MAX_NR_INTERFACES]; 235 | long long tx_errors[MAX_NR_INTERFACES]; 236 | long long tx_drops[MAX_NR_INTERFACES]; 237 | char mac_addr[MAX_NR_INTERFACES][6]; 238 | } netinfo_t; 239 | 240 | 241 | typedef struct ipinfo_s { 242 | long long ipForwarding; 243 | long long ipDefaultTTL; 244 | long long ipReasmTimeout; 245 | } ipinfo_t; 246 | 247 | typedef struct tcpinfo_s { 248 | long long tcpRtoAlgorithm; 249 | long long tcpRtoMin; 250 | long long tcpRtoMax; 251 | long long tcpMaxConn; 252 | long long tcpActiveOpens; 253 | long long tcpPassiveOpens; 254 | long long tcpAttemptFails; 255 | long long tcpEstabResets; 256 | long long tcpCurrEstab; 257 | long long tcpInSegs; 258 | long long tcpOutSegs; 259 | long long tcpRetransSegs; 260 | long long tcpInErrs; 261 | long long tcpOutRsts; 262 | } tcpinfo_t; 263 | 264 | typedef struct udpinfo_s { 265 | long long udpInDatagrams; 266 | long long udpNoPorts; 267 | long long udpInErrors; 268 | long long udpOutDatagrams; 269 | } udpinfo_t; 270 | 271 | #ifdef CONFIG_ENABLE_DEMO 272 | typedef struct demoinfo_s { 273 | unsigned int random_value_1; 274 | unsigned int random_value_2; 275 | } demoinfo_t; 276 | #endif 277 | 278 | 279 | /* 280 | * Global variables 281 | */ 282 | 283 | extern const struct in_addr inaddr_any; 284 | 285 | extern int g_family; 286 | extern int g_timeout; 287 | extern int g_auth; 288 | extern int g_daemon; 289 | extern int g_syslog; 290 | extern int g_level; 291 | extern volatile sig_atomic_t g_quit; 292 | 293 | extern char *g_prognm; 294 | extern char *g_community; 295 | extern char *g_description; 296 | extern char *g_vendor; 297 | extern char *g_location; 298 | extern char *g_contact; 299 | extern char *g_bind_to_device; 300 | extern char *g_user; 301 | 302 | extern char *g_disk_list[MAX_NR_DISKS]; 303 | extern size_t g_disk_list_length; 304 | 305 | extern char *g_interface_list[MAX_NR_INTERFACES]; 306 | extern size_t g_interface_list_length; 307 | 308 | extern in_port_t g_udp_port; 309 | extern in_port_t g_tcp_port; 310 | 311 | extern client_t g_udp_client; 312 | extern client_t *g_tcp_client_list[MAX_NR_CLIENTS]; 313 | extern size_t g_tcp_client_list_length; 314 | 315 | extern int g_udp_sockfd; 316 | extern int g_tcp_sockfd; 317 | 318 | extern value_t g_mib[MAX_NR_VALUES]; 319 | extern size_t g_mib_length; 320 | 321 | /* 322 | * Functions 323 | */ 324 | 325 | void dump_packet (const client_t *client); 326 | void dump_mib (const value_t *value, int size); 327 | void dump_response (const response_t *response); 328 | 329 | char *oid_ntoa (const oid_t *oid); 330 | oid_t *oid_aton (const char *str); 331 | int oid_cmp (const oid_t *oid1, const oid_t *oid2); 332 | 333 | int split(const char *str, char *delim, char **list, int max_list_length); 334 | 335 | client_t *find_oldest_client(void); 336 | int find_ifname(char *ifname); 337 | 338 | void *allocate (size_t len); 339 | 340 | int read_config (char *file); 341 | 342 | int parse_file (char *file, field_t fields[], size_t limit, size_t skip_prefix); 343 | int read_file (const char *filename, char *buffer, size_t size); 344 | 345 | unsigned int read_value (const char *buffer, const char *prefix); 346 | void read_values (const char *buffer, const char *prefix, unsigned int *values, int count); 347 | 348 | int read_file_value(unsigned int *val, const char *fmt, ...); 349 | 350 | int ticks_since (const struct timeval *tv_last, struct timeval *tv_now); 351 | 352 | unsigned int get_process_uptime (void); 353 | unsigned int get_system_uptime (void); 354 | 355 | void get_loadinfo (loadinfo_t *loadinfo); 356 | void get_meminfo (meminfo_t *meminfo); 357 | void get_cpuinfo (cpuinfo_t *cpuinfo); 358 | void get_ipinfo (ipinfo_t *ipinfo); 359 | void get_tcpinfo (tcpinfo_t *tcpinfo); 360 | void get_udpinfo (udpinfo_t *udpinfo); 361 | void get_diskinfo (diskinfo_t *diskinfo); 362 | void get_netinfo (netinfo_t *netinfo); 363 | #ifdef CONFIG_ENABLE_DEMO 364 | void get_demoinfo (demoinfo_t *demoinfo); 365 | #endif 366 | int logit (int priority, int syserr, const char *fmt, ...); 367 | 368 | int snmp_packet_complete (const client_t *client); 369 | int snmp ( client_t *client); 370 | int snmp_element_as_string (const data_t *data, char *buffer, size_t size); 371 | 372 | int mib_build (void); 373 | int mib_update (int full); 374 | 375 | value_t *mib_find (const oid_t *oid, size_t *pos); 376 | value_t *mib_findnext (const oid_t *oid); 377 | 378 | #ifdef CONFIG_ENABLE_ETHTOOL 379 | int ethtool_gstats(int intf, netinfo_t *netinfo, field_t *field); 380 | #else 381 | #define ethtool_gstats(intf, netinfo, field) (-1) 382 | #endif 383 | 384 | #endif /* MINI_SNMPD_H_ */ 385 | 386 | /* vim: ts=4 sts=4 sw=4 nowrap 387 | */ 388 | -------------------------------------------------------------------------------- /mini-snmpd.service.in: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Small SNMP daemon 3 | Documentation=man:mini-snmpd 4 | After=network-online.target 5 | Requires=network-online.target 6 | 7 | [Service] 8 | EnvironmentFile=-/etc/default/mini-snmpd 9 | Type=simple 10 | ExecStart=@SBINDIR@/mini-snmpd -ns $DAEMON_OPTS 11 | StandardOutput=null 12 | Restart=on-failure 13 | 14 | [Install] 15 | WantedBy=multi-user.target 16 | Alias=snmpd.service 17 | -------------------------------------------------------------------------------- /protocol.c: -------------------------------------------------------------------------------- 1 | /* SNMP protocol 2 | * 3 | * Copyright (C) 2008-2010 Robert Ernst 4 | * Copyright (C) 2011 Javier Palacios 5 | * Copyright (C) 2015-2020 Joachim Nilsson 6 | * 7 | * This file may be distributed and/or modified under the terms of the 8 | * GNU General Public License version 2 as published by the Free Software 9 | * Foundation and appearing in the file LICENSE.GPL included in the 10 | * packaging of this file. 11 | * 12 | * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE 13 | * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 14 | * 15 | * See COPYING for GPL licensing information. 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "mini-snmpd.h" 25 | 26 | #define SNMP_VERSION_1_ERROR(resp, code, index) { \ 27 | (resp)->error_status = code; \ 28 | (resp)->error_index = index; \ 29 | return 0; \ 30 | } 31 | 32 | #define SNMP_VERSION_2_ERROR(resp, req, index, err) { \ 33 | size_t len = (resp)->value_list_length; \ 34 | memcpy(&(resp)->value_list[len].oid, &(req)->oid_list[index], \ 35 | sizeof((req)->oid_list[index])); \ 36 | memcpy(&(resp)->value_list[len].data, &err, sizeof(err)); \ 37 | (resp)->value_list_length++; \ 38 | continue; \ 39 | } 40 | 41 | #define SNMP_GET_ERROR(resp, req, index, code, err, msg) { \ 42 | if ((req)->version == SNMP_VERSION_1) \ 43 | SNMP_VERSION_1_ERROR((resp), (code), (index)); \ 44 | \ 45 | if ((resp)->value_list_length < MAX_NR_VALUES) \ 46 | SNMP_VERSION_2_ERROR((resp), (req), (index), err); \ 47 | \ 48 | logit(LOG_ERR, 0, "%s", msg); \ 49 | return -1; \ 50 | } 51 | 52 | static const data_t m_null = { (unsigned char *)"\x05\x00", 2, 2 }; 53 | static const data_t m_no_such_object = { (unsigned char *)"\x80\x00", 2, 2 }; 54 | static const data_t m_no_such_instance = { (unsigned char *)"\x81\x00", 2, 2 }; 55 | static const data_t m_end_of_mib_view = { (unsigned char *)"\x82\x00", 2, 2 }; 56 | 57 | 58 | static int decode_len(const unsigned char *packet, size_t size, size_t *pos, int *type, size_t *len) 59 | { 60 | size_t length_of_len; 61 | 62 | if (*pos >= size) { 63 | logit(LOG_DEBUG, 0, "underflow for element type"); 64 | errno = EINVAL; 65 | return -1; 66 | } 67 | 68 | /* Fetch the ASN.1 element type (only subset of universal tags supported) */ 69 | switch (packet[*pos]) { 70 | case BER_TYPE_BOOLEAN: 71 | case BER_TYPE_INTEGER: 72 | case BER_TYPE_BIT_STRING: 73 | case BER_TYPE_OCTET_STRING: 74 | case BER_TYPE_NULL: 75 | case BER_TYPE_OID: 76 | case BER_TYPE_SEQUENCE: 77 | case BER_TYPE_COUNTER: 78 | case BER_TYPE_GAUGE: 79 | case BER_TYPE_TIME_TICKS: 80 | case BER_TYPE_NO_SUCH_OBJECT: 81 | case BER_TYPE_NO_SUCH_INSTANCE: 82 | case BER_TYPE_END_OF_MIB_VIEW: 83 | case BER_TYPE_SNMP_GET: 84 | case BER_TYPE_SNMP_GETNEXT: 85 | case BER_TYPE_SNMP_RESPONSE: 86 | case BER_TYPE_SNMP_SET: 87 | case BER_TYPE_SNMP_GETBULK: 88 | case BER_TYPE_SNMP_INFORM: 89 | case BER_TYPE_SNMP_TRAP: 90 | *type = packet[*pos]; 91 | *pos = *pos + 1; 92 | break; 93 | 94 | default: 95 | logit(LOG_DEBUG, 0, "unsupported element type %02X", packet[*pos]); 96 | errno = EINVAL; 97 | return -1; 98 | } 99 | 100 | if (*pos >= size) { 101 | logit(LOG_DEBUG, 0, "underflow for element length"); 102 | errno = EINVAL; 103 | return -1; 104 | } 105 | 106 | /* Fetch the ASN.1 element length (only lengths up to 16 bit supported) */ 107 | if (!(packet[*pos] & 0x80)) { 108 | *len = packet[*pos]; 109 | *pos = *pos + 1; 110 | } else { 111 | length_of_len = packet[*pos] & 0x7F; 112 | if (length_of_len > 2) { 113 | logit(LOG_DEBUG, 0, "overflow for element length"); 114 | errno = EINVAL; 115 | return -1; 116 | } 117 | 118 | *pos = *pos + 1; 119 | *len = 0; 120 | while (length_of_len--) { 121 | if (*pos >= size) { 122 | logit(LOG_DEBUG, 0, "underflow for element length"); 123 | errno = EINVAL; 124 | return -1; 125 | } 126 | 127 | *len = (*len << 8) + packet[*pos]; 128 | *pos = *pos + 1; 129 | } 130 | } 131 | 132 | return 0; 133 | } 134 | 135 | /* Fetch the value as unsigned integer (copy sign bit into all bytes first) */ 136 | static int decode_int(const unsigned char *packet, size_t size, size_t *pos, size_t len, int *value) 137 | { 138 | unsigned int tmp; 139 | 140 | if (len > size || *pos >= (size - len + 1)) { 141 | logit(LOG_DEBUG, 0, "underflow for integer"); 142 | errno = EINVAL; 143 | return -1; 144 | } 145 | 146 | memset(&tmp, (packet[*pos] & 0x80) ? 0xFF : 0x00, sizeof(tmp)); 147 | while (len--) { 148 | tmp = (tmp << 8) | packet[*pos]; 149 | *pos = *pos + 1; 150 | } 151 | *(int *)value = tmp; 152 | 153 | return 0; 154 | } 155 | 156 | /* Fetch the value as unsigned integer (copy sign bit into all bytes first) */ 157 | static int decode_cnt(const unsigned char *packet, size_t size, size_t *pos, size_t len, uint32_t *value) 158 | { 159 | if (len > size || *pos >= (size - len + 1)) { 160 | logit(LOG_DEBUG, 0, "underflow for unsigned"); 161 | errno = EINVAL; 162 | return -1; 163 | } 164 | 165 | *value = 0; 166 | while (len--) { 167 | *value = (*value << 8) | packet[*pos]; 168 | *pos = *pos + 1; 169 | } 170 | 171 | return 0; 172 | } 173 | 174 | /* Fetch the value as C string (user must have made sure the length is ok) */ 175 | static int decode_str(const unsigned char *packet, size_t size, size_t *pos, size_t len, char *str, size_t str_len) 176 | { 177 | if (*pos >= (size - len + 1)) { 178 | logit(LOG_DEBUG, 0, "underflow for string"); 179 | errno = EINVAL; 180 | return -1; 181 | } 182 | 183 | snprintf(str, str_len, "%.*s", (int)len, &packet[*pos]); 184 | *pos = *pos + len; 185 | 186 | return 0; 187 | } 188 | 189 | /* Fetch the value as C string (user must have made sure the length is ok) */ 190 | static int decode_oid(const unsigned char *packet, size_t size, size_t *pos, size_t len, oid_t *value) 191 | { 192 | if (*pos >= (size - len + 1)) { 193 | logit(LOG_DEBUG, 0, "underflow for oid"); 194 | errno = EINVAL; 195 | return -1; 196 | } 197 | 198 | value->encoded_length = len; 199 | if (len > 0xFFFF) { 200 | logit(LOG_ERR, 0, "could not decode: internal error"); 201 | return -1; 202 | } 203 | 204 | if (len > 0xFF) 205 | value->encoded_length += 4; 206 | else if (len > 0x7F) 207 | value->encoded_length += 3; 208 | else 209 | value->encoded_length += 2; 210 | 211 | value->subid_list_length = 0; 212 | if (!len) { 213 | logit(LOG_DEBUG, 0, "underflow for OID startbyte"); 214 | errno = EINVAL; 215 | return -1; 216 | } 217 | 218 | if (packet[*pos] & 0x80) { 219 | logit(LOG_DEBUG, 0, "unsupported OID startbyte %02X", packet[*pos]); 220 | errno = EINVAL; 221 | return -1; 222 | } 223 | 224 | value->subid_list[value->subid_list_length++] = packet[*pos] / 40; 225 | value->subid_list[value->subid_list_length++] = packet[*pos] % 40; 226 | *pos = *pos + 1; 227 | len--; 228 | 229 | while (len) { 230 | if (value->subid_list_length >= MAX_NR_SUBIDS) { 231 | logit(LOG_DEBUG, 0, "overflow for OID byte"); 232 | errno = EFAULT; 233 | return -1; 234 | } 235 | 236 | value->subid_list[value->subid_list_length] = 0; 237 | while (len--) { 238 | value->subid_list[value->subid_list_length] 239 | = (value->subid_list[value->subid_list_length] << 7) + (packet[*pos] & 0x7F); 240 | if (packet[*pos] & 0x80) { 241 | if (!len) { 242 | logit(LOG_DEBUG, 0, "underflow for OID byte"); 243 | errno = EINVAL; 244 | return -1; 245 | } 246 | *pos = *pos + 1; 247 | } else { 248 | *pos = *pos + 1; 249 | break; 250 | } 251 | } 252 | value->subid_list_length++; 253 | } 254 | 255 | return 0; 256 | } 257 | 258 | /* Fetch the value as pointer (user must make sure not to overwrite packet) */ 259 | static int decode_ptr(const unsigned char UNUSED(*packet), size_t size, size_t *pos, int len) 260 | { 261 | if (*pos >= (size - len + 1)) { 262 | logit(LOG_DEBUG, 0, "underflow for ptr"); 263 | errno = EINVAL; 264 | return -1; 265 | } 266 | 267 | *pos = *pos + len; 268 | 269 | return 0; 270 | } 271 | 272 | static int decode_snmp_request(request_t *request, client_t *client) 273 | { 274 | int type; 275 | size_t pos = 0, len = 0; 276 | const char *header_msg = "Unexpected SNMP header"; 277 | const char *error_msg = "Unexpected SNMP error"; 278 | const char *request_msg = "Unexpected SNMP request"; 279 | const char *varbind_msg = "Unexpected SNMP varbindings"; 280 | const char *commun_msg = "SNMP community"; 281 | const char *version_msg = "SNMP version"; 282 | 283 | /* The SNMP message is enclosed in a sequence */ 284 | if (decode_len(client->packet, client->size, &pos, &type, &len) == -1) 285 | return -1; 286 | 287 | if (type != BER_TYPE_SEQUENCE || len != (client->size - pos)) { 288 | logit(LOG_DEBUG, 0, "%s type %02X length %zu", header_msg, type, len); 289 | errno = EINVAL; 290 | return -1; 291 | } 292 | 293 | /* The first element of the sequence is the version */ 294 | if (decode_len(client->packet, client->size, &pos, &type, &len) == -1) 295 | return -1; 296 | 297 | if (type != BER_TYPE_INTEGER || len != 1) { 298 | logit(LOG_DEBUG, 0, "Unexpected %s type %02X length %zu", version_msg, type, len); 299 | errno = EINVAL; 300 | return -1; 301 | } 302 | 303 | if (decode_int(client->packet, client->size, &pos, len, &request->version) == -1) 304 | return -1; 305 | 306 | if (request->version != SNMP_VERSION_1 && request->version != SNMP_VERSION_2C) { 307 | logit(LOG_DEBUG, 0, "Unsupported %s %d", version_msg, request->version); 308 | errno = EINVAL; 309 | return -1; 310 | } 311 | 312 | /* The second element of the sequence is the community string */ 313 | if (decode_len(client->packet, client->size, &pos, &type, &len) == -1) 314 | return -1; 315 | 316 | if (type != BER_TYPE_OCTET_STRING || len >= sizeof(request->community)) { 317 | logit(LOG_DEBUG, 0, "Unexpected %s type %02X length %zu", commun_msg, type, len); 318 | errno = EINVAL; 319 | return -1; 320 | } 321 | 322 | if (decode_str(client->packet, client->size, &pos, len, request->community, sizeof(request->community)) == -1) 323 | return -1; 324 | 325 | if (strlen(request->community) < 1) { 326 | logit(LOG_DEBUG, 0, "unsupported %s '%s'", commun_msg, request->community); 327 | errno = EINVAL; 328 | return -1; 329 | } 330 | 331 | /* The third element of the sequence is the SNMP request */ 332 | if (decode_len(client->packet, client->size, &pos, &type, &len) == -1) 333 | return -1; 334 | 335 | if (len != (client->size - pos)) { 336 | logit(LOG_DEBUG, 0, "%s type type %02X length %zu", request_msg, type, len); 337 | errno = EINVAL; 338 | return -1; 339 | } 340 | request->type = type; 341 | 342 | /* The first element of the SNMP request is the request ID */ 343 | if (decode_len(client->packet, client->size, &pos, &type, &len) == -1) 344 | return -1; 345 | 346 | if (type != BER_TYPE_INTEGER || len < 1) { 347 | logit(LOG_DEBUG, 0, "%s id type %02X length %zu", request_msg, type, len); 348 | errno = EINVAL; 349 | return -1; 350 | } 351 | 352 | if (decode_int(client->packet, client->size, &pos, len, &request->id) == -1) 353 | return -1; 354 | 355 | /* The second element of the SNMP request is the error state / non repeaters (0..2147483647) */ 356 | if (decode_len(client->packet, client->size, &pos, &type, &len) == -1) 357 | return -1; 358 | 359 | if (type != BER_TYPE_INTEGER || len < 1) { 360 | logit(LOG_DEBUG, 0, "%s state type %02X length %zu", error_msg, type, len); 361 | errno = EINVAL; 362 | return -1; 363 | } 364 | 365 | if (decode_cnt(client->packet, client->size, &pos, len, &request->non_repeaters) == -1) 366 | return -1; 367 | 368 | /* The third element of the SNMP request is the error index / max repetitions (0..2147483647) */ 369 | if (decode_len(client->packet, client->size, &pos, &type, &len) == -1) 370 | return -1; 371 | 372 | if (type != BER_TYPE_INTEGER || len < 1) { 373 | logit(LOG_DEBUG, 0, "%s index type %02X length %zu", error_msg, type, len); 374 | errno = EINVAL; 375 | return -1; 376 | } 377 | 378 | if (decode_cnt(client->packet, client->size, &pos, len, &request->max_repetitions) == -1) 379 | return -1; 380 | 381 | /* The fourth element of the SNMP request are the variable bindings */ 382 | if (decode_len(client->packet, client->size, &pos, &type, &len) == -1) 383 | return -1; 384 | 385 | if (type != BER_TYPE_SEQUENCE || len != (client->size - pos)) { 386 | logit(LOG_DEBUG, 0, "%s type %02X length %zu", varbind_msg, type, len); 387 | errno = EINVAL; 388 | return -1; 389 | } 390 | 391 | /* Loop through the variable bindings */ 392 | request->oid_list_length = 0; 393 | while (pos < client->size) { 394 | /* If there is not enough room in the OID list, bail out now */ 395 | if (request->oid_list_length >= MAX_NR_OIDS) { 396 | logit(LOG_DEBUG, 0, "Overflow in OID list"); 397 | errno = EFAULT; 398 | return -1; 399 | } 400 | 401 | /* Each variable binding is a sequence describing the variable */ 402 | if (decode_len(client->packet, client->size, &pos, &type, &len) == -1) 403 | return -1; 404 | 405 | if (type != BER_TYPE_SEQUENCE || len < 1) { 406 | logit(LOG_DEBUG, 0, "%s type %02X length %zu", varbind_msg, type, len); 407 | errno = EINVAL; 408 | return -1; 409 | } 410 | 411 | /* The first element of the variable binding is the OID */ 412 | if (decode_len(client->packet, client->size, &pos, &type, &len) == -1) 413 | return -1; 414 | 415 | if (type != BER_TYPE_OID || len < 1) { 416 | logit(LOG_DEBUG, 0, "%s OID type %02X length %zu", varbind_msg, type, len); 417 | errno = EINVAL; 418 | return -1; 419 | } 420 | 421 | if (decode_oid(client->packet, client->size, &pos, len, &request->oid_list[request->oid_list_length]) == -1) 422 | return -1; 423 | 424 | /* The second element of the variable binding is the new type and value */ 425 | if (decode_len(client->packet, client->size, &pos, &type, &len) == -1) 426 | return -1; 427 | 428 | if ((type == BER_TYPE_NULL && len) || (type != BER_TYPE_NULL && !len)) { 429 | logit(LOG_DEBUG, 0, "%s value type %02X length %zu", varbind_msg, type, len); 430 | errno = EINVAL; 431 | return -1; 432 | } 433 | 434 | if (decode_ptr(client->packet, client->size, &pos, len) == -1) 435 | return -1; 436 | 437 | /* Now the OID list has one more entry */ 438 | request->oid_list_length++; 439 | } 440 | 441 | return 0; 442 | } 443 | 444 | 445 | static size_t get_intlen(int val) 446 | { 447 | if (val < -8388608 || val > 8388607) 448 | return 6; 449 | if (val < -32768 || val > 32767) 450 | return 5; 451 | if (val < -128 || val > 127) 452 | return 4; 453 | 454 | return 3; 455 | } 456 | 457 | static size_t get_strlen(const char *str) 458 | { 459 | size_t len = strlen(str); 460 | 461 | if (len > 0xFFFF) 462 | return MAX_PACKET_SIZE; 463 | if (len > 0xFF) 464 | return len + 4; 465 | if (len > 0x7F) 466 | return len + 3; 467 | 468 | return len + 2; 469 | } 470 | 471 | static size_t get_hdrlen(size_t len) 472 | { 473 | if (len > 0xFFFF) 474 | return MAX_PACKET_SIZE; 475 | if (len > 0xFF) 476 | return 4; 477 | if (len > 0x7F) 478 | return 3; 479 | 480 | return 2; 481 | } 482 | 483 | static int encode_snmp_integer(unsigned char *buf, int val) 484 | { 485 | size_t len; 486 | 487 | if (val < -8388608 || val > 8388607) 488 | len = 4; 489 | else if (val < -32768 || val > 32767) 490 | len = 3; 491 | else if (val < -128 || val > 127) 492 | len = 2; 493 | else 494 | len = 1; 495 | 496 | *buf++ = BER_TYPE_INTEGER; 497 | *buf++ = len; 498 | while (len--) 499 | *buf++ = ((unsigned int)val >> (8 * len)) & 0xFF; 500 | 501 | return 0; 502 | } 503 | 504 | static int encode_snmp_string(unsigned char *buf, const char *str) 505 | { 506 | size_t len; 507 | 508 | len = strlen(str); 509 | if (len > 0xFFFF) 510 | return -1; 511 | 512 | *buf++ = BER_TYPE_OCTET_STRING; 513 | if (len > 0xFF) { 514 | *buf++ = 0x82; 515 | *buf++ = (len >> 8) & 0xFF; 516 | *buf++ = len & 0xFF; 517 | } else if (len > 0x7F) { 518 | *buf++= 0x81; 519 | *buf++ = len & 0xFF; 520 | } else { 521 | *buf++ = len & 0x7F; 522 | } 523 | memcpy(buf, str, len); 524 | 525 | return 0; 526 | } 527 | 528 | static int encode_snmp_sequence_header(unsigned char *buf, size_t len, int type) 529 | { 530 | if (len > 0xFFFF) 531 | return -1; 532 | 533 | *buf++ = type; 534 | if (len > 0xFF) { 535 | *buf++ = 0x82; 536 | *buf++ = (len >> 8) & 0xFF; 537 | *buf++ = len & 0xFF; 538 | } else if (len > 0x7F) { 539 | *buf++= 0x81; 540 | *buf++ = len & 0xFF; 541 | } else { 542 | *buf++ = len & 0x7F; 543 | } 544 | 545 | return 0; 546 | } 547 | 548 | static int encode_snmp_oid(unsigned char *buf, const oid_t *oid) 549 | { 550 | size_t i, len; 551 | 552 | len = 1; 553 | for (i = 2; i < oid->subid_list_length; i++) { 554 | if (oid->subid_list[i] >= (1 << 28)) 555 | len += 5; 556 | else if (oid->subid_list[i] >= (1 << 21)) 557 | len += 4; 558 | else if (oid->subid_list[i] >= (1 << 14)) 559 | len += 3; 560 | else if (oid->subid_list[i] >= (1 << 7)) 561 | len += 2; 562 | else 563 | len += 1; 564 | } 565 | 566 | *buf++ = BER_TYPE_OID; 567 | if (len > 0xFFFF) { 568 | logit(LOG_ERR, 0, "could not encode '%s': OID overflow", oid_ntoa(oid)); 569 | return -1; 570 | } 571 | 572 | if (len > 0xFF) { 573 | *buf++ = 0x82; 574 | *buf++ = (len >> 8) & 0xFF; 575 | *buf++ = len & 0xFF; 576 | } else if (len > 0x7F) { 577 | *buf++ = 0x81; 578 | *buf++ = len & 0xFF; 579 | } else { 580 | *buf++ = len & 0x7F; 581 | } 582 | 583 | *buf++ = oid->subid_list[0] * 40 + oid->subid_list[1]; 584 | for (i = 2; i < oid->subid_list_length; i++) { 585 | if (oid->subid_list[i] >= (1 << 28)) 586 | len = 5; 587 | else if (oid->subid_list[i] >= (1 << 21)) 588 | len = 4; 589 | else if (oid->subid_list[i] >= (1 << 14)) 590 | len = 3; 591 | else if (oid->subid_list[i] >= (1 << 7)) 592 | len = 2; 593 | else 594 | len = 1; 595 | 596 | while (len--) { 597 | if (len) 598 | *buf++ = ((oid->subid_list[i] >> (7 * len)) & 0x7F) | 0x80; 599 | else 600 | *buf++ = (oid->subid_list[i] >> (7 * len)) & 0x7F; 601 | } 602 | } 603 | 604 | return 0; 605 | } 606 | 607 | static int log_encoding_error(const char *what, const char *why) 608 | { 609 | logit(LOG_ERR, 0, "Failed encoding %s: %s", what, why); 610 | return -1; 611 | } 612 | 613 | static int encode_snmp_varbind(unsigned char *buf, size_t *pos, const value_t *value) 614 | { 615 | size_t len; 616 | 617 | /* The value of the variable binding (NULL for error responses) */ 618 | len = value->data.encoded_length; 619 | if (*pos < len) 620 | return log_encoding_error(oid_ntoa(&value->oid), "DATA overflow"); 621 | 622 | memcpy(&buf[*pos - len], value->data.buffer, len); 623 | *pos = *pos - len; 624 | 625 | /* The OID of the variable binding */ 626 | len = value->oid.encoded_length; 627 | if (*pos < len) 628 | return log_encoding_error(oid_ntoa(&value->oid), "OID overflow"); 629 | 630 | encode_snmp_oid(&buf[*pos - len], &value->oid); 631 | *pos = *pos - len; 632 | 633 | /* The sequence header (type and length) of the variable binding */ 634 | len = get_hdrlen(value->oid.encoded_length + value->data.encoded_length); 635 | if (*pos < len) 636 | return log_encoding_error(oid_ntoa(&value->oid), "VARBIND overflow"); 637 | 638 | encode_snmp_sequence_header(&buf[*pos - len], value->oid.encoded_length + value->data.encoded_length, BER_TYPE_SEQUENCE); 639 | *pos = *pos - len; 640 | 641 | return 0; 642 | } 643 | 644 | static int encode_snmp_response(request_t *request, response_t *response, client_t *client) 645 | { 646 | size_t i, len, pos; 647 | 648 | /* If there was an error, we have to encode the original varbind list, but 649 | * omit any varbind values (replace them with NULL values) 650 | */ 651 | if (response->error_status != SNMP_STATUS_OK) { 652 | if (request->oid_list_length > MAX_NR_VALUES) 653 | return log_encoding_error("SNMP response", "value list overflow"); 654 | 655 | for (i = 0; i < request->oid_list_length && i < NELEMS(request->oid_list); i++) { 656 | memcpy(&response->value_list[i].oid, &request->oid_list[i], sizeof(request->oid_list[i])); 657 | memcpy(&response->value_list[i].data, &m_null, sizeof(m_null)); 658 | } 659 | response->value_list_length = request->oid_list_length; 660 | } 661 | 662 | /* Dump the response for debugging purposes */ 663 | #ifdef DEBUG 664 | dump_response(response); 665 | #endif 666 | 667 | /* To make the code more compact and save processing time, we are encoding the 668 | * data beginning at the last byte of the buffer backwards. Thus, the encoded 669 | * packet will not be positioned at offset 0..(size-1) of the client's packet 670 | * buffer, but at offset (bufsize-size..bufsize-1)! 671 | */ 672 | pos = MAX_PACKET_SIZE; 673 | for (i = response->value_list_length; i > 0; i--) { 674 | if (encode_snmp_varbind(client->packet, &pos, &response->value_list[i-1]) == -1) 675 | return -1; 676 | } 677 | 678 | len = get_hdrlen(MAX_PACKET_SIZE - pos); 679 | if (pos < len) 680 | return log_encoding_error("SNMP response", "VARBINDS overflow"); 681 | 682 | encode_snmp_sequence_header(&client->packet[pos - len], MAX_PACKET_SIZE - pos, BER_TYPE_SEQUENCE); 683 | pos = pos - len; 684 | 685 | len = get_intlen(response->error_index); 686 | if (pos < len) 687 | return log_encoding_error("SNMP response", "ERROR INDEX overflow"); 688 | 689 | encode_snmp_integer(&client->packet[pos - len], response->error_index); 690 | pos = pos - len; 691 | 692 | len = get_intlen(response->error_status); 693 | if (pos < len) 694 | return log_encoding_error("SNMP response", "ERROR STATUS overflow"); 695 | 696 | encode_snmp_integer(&client->packet[pos - len], response->error_status); 697 | pos = pos - len; 698 | 699 | len = get_intlen(request->id); 700 | if (pos < len) 701 | return log_encoding_error("SNMP response", "ID overflow"); 702 | 703 | encode_snmp_integer(&client->packet[pos - len], request->id); 704 | pos = pos - len; 705 | 706 | len = get_hdrlen(MAX_PACKET_SIZE - pos); 707 | if (pos < len) 708 | return log_encoding_error("SNMP response", "PDU overflow"); 709 | 710 | encode_snmp_sequence_header(&client->packet[pos - len], MAX_PACKET_SIZE - pos, BER_TYPE_SNMP_RESPONSE); 711 | pos = pos - len; 712 | 713 | len = get_strlen(request->community); 714 | if (pos < len) 715 | return log_encoding_error("SNMP response", "COMMUNITY overflow"); 716 | 717 | encode_snmp_string(&client->packet[pos - len], request->community); 718 | pos = pos - len; 719 | 720 | len = get_intlen(request->version); 721 | if (pos < len) 722 | return log_encoding_error("SNMP response", "VERSION overflow"); 723 | 724 | encode_snmp_integer(&client->packet[pos - len], request->version); 725 | pos = pos - len; 726 | 727 | len = get_hdrlen(MAX_PACKET_SIZE - pos); 728 | if (pos < len) 729 | return log_encoding_error("SNMP response", "RESPONSE overflow"); 730 | 731 | encode_snmp_sequence_header(&client->packet[pos - len], MAX_PACKET_SIZE - pos, BER_TYPE_SEQUENCE); 732 | pos = pos - len; 733 | 734 | /* 735 | * Now move the packet to the start of the buffer so that the caller does not have 736 | * to deal with this messy detail (the CPU cycles needed are worth their money!) 737 | * and set up the packet size. 738 | */ 739 | if (pos > 0) 740 | memmove(&client->packet[0], &client->packet[pos], MAX_PACKET_SIZE - pos); 741 | client->size = MAX_PACKET_SIZE - pos; 742 | 743 | return 0; 744 | } 745 | 746 | static int handle_snmp_get(request_t *request, response_t *response, client_t *UNUSED(client)) 747 | { 748 | size_t i, pos; 749 | value_t *value; 750 | const char *msg = "Failed handling SNMP GET: value list overflow\n"; 751 | 752 | /* 753 | * Search each varbinding of the request and append the value to the 754 | * response. Note that if the length does not match, we might have found a 755 | * subid of the requested one (table cell of table column)! 756 | */ 757 | for (i = 0; i < request->oid_list_length; i++) { 758 | pos = 0; 759 | value = mib_find(&request->oid_list[i], &pos); 760 | if (!value) 761 | SNMP_GET_ERROR(response, request, i, SNMP_STATUS_NO_SUCH_NAME, m_no_such_object, msg); 762 | 763 | if (pos >= g_mib_length) 764 | SNMP_GET_ERROR(response, request, i, SNMP_STATUS_NO_SUCH_NAME, m_no_such_object, msg); 765 | 766 | if (value->oid.subid_list_length == (request->oid_list[i].subid_list_length + 1)) 767 | SNMP_GET_ERROR(response, request, i, SNMP_STATUS_NO_SUCH_NAME, m_no_such_instance, msg); 768 | 769 | if (value->oid.subid_list_length != request->oid_list[i].subid_list_length) 770 | SNMP_GET_ERROR(response, request, i, SNMP_STATUS_NO_SUCH_NAME, m_no_such_object, msg); 771 | 772 | if (response->value_list_length < MAX_NR_VALUES) { 773 | memcpy(&response->value_list[response->value_list_length], value, sizeof(*value)); 774 | response->value_list_length++; 775 | continue; 776 | } 777 | 778 | logit(LOG_ERR, 0, "%s", msg); 779 | return -1; 780 | } 781 | 782 | return 0; 783 | } 784 | 785 | static int handle_snmp_getnext(request_t *request, response_t *response, client_t *UNUSED(client)) 786 | { 787 | size_t i; 788 | value_t *value; 789 | const char *msg = "Failed handling SNMP GETNEXT: value list overflow\n"; 790 | 791 | /* 792 | * Search each varbinding of the request and append the value to the 793 | * response. Note that if the length does not match, we might have found a 794 | * subid of the requested one (table cell of table column)! 795 | */ 796 | for (i = 0; i < request->oid_list_length; i++) { 797 | value = mib_findnext(&request->oid_list[i]); 798 | if (!value) 799 | SNMP_GET_ERROR(response, request, i, SNMP_STATUS_NO_SUCH_NAME, m_end_of_mib_view, msg); 800 | 801 | if (response->value_list_length < MAX_NR_VALUES) { 802 | memcpy(&response->value_list[response->value_list_length], value, sizeof(*value)); 803 | response->value_list_length++; 804 | continue; 805 | } 806 | 807 | logit(LOG_ERR, 0, "%s", msg); 808 | return -1; 809 | } 810 | 811 | return 0; 812 | } 813 | 814 | static int handle_snmp_set(request_t *request, response_t *response, client_t *UNUSED(client)) 815 | { 816 | SNMP_VERSION_1_ERROR(response, (request->version == SNMP_VERSION_1) 817 | ? SNMP_STATUS_NO_SUCH_NAME : SNMP_STATUS_NO_ACCESS, 0); 818 | } 819 | 820 | static int handle_snmp_getbulk(request_t *request, response_t *response, client_t *UNUSED(client)) 821 | { 822 | size_t i, j; 823 | oid_t oid_list[MAX_NR_OIDS]; 824 | value_t *value; 825 | const char *msg = "Failed handling SNMP GETBULK: value list overflow\n"; 826 | 827 | /* Make a local copy of the OID list since we are going to modify it */ 828 | memcpy(oid_list, request->oid_list, sizeof(request->oid_list)); 829 | 830 | /* The non-repeaters are handled like with the GETNEXT request */ 831 | for (i = 0; i < request->oid_list_length; i++) { 832 | if (i >= request->non_repeaters) 833 | break; 834 | 835 | value = mib_findnext(&oid_list[i]); 836 | if (!value) 837 | SNMP_GET_ERROR(response, request, i, SNMP_STATUS_NO_SUCH_NAME, m_end_of_mib_view, msg); 838 | 839 | if (response->value_list_length < MAX_NR_VALUES) { 840 | memcpy(&response->value_list[response->value_list_length], value, sizeof(*value)); 841 | response->value_list_length++; 842 | continue; 843 | } 844 | 845 | logit(LOG_ERR, 0, "%s", msg); 846 | return -1; 847 | } 848 | 849 | /* 850 | * The repeaters are handled like with the GETNEXT request, except that: 851 | * 852 | * - the access is interleaved (i.e. first repetition of all varbinds, 853 | * then second repetition of all varbinds, then third,...) 854 | * - the repetitions are aborted as soon as there is no successor found 855 | * for all of the varbinds 856 | * - other than with getnext, the last variable in the MIB is named if 857 | * the variable queried is not after the end of the MIB 858 | */ 859 | for (j = 0; j < request->max_repetitions; j++) { 860 | int found_repeater = 0; 861 | 862 | for (i = request->non_repeaters; i < request->oid_list_length; i++) { 863 | value = mib_findnext(&oid_list[i]); 864 | if (!value) 865 | SNMP_GET_ERROR(response, request, i, SNMP_STATUS_NO_SUCH_NAME, m_end_of_mib_view, msg); 866 | 867 | if (response->value_list_length < MAX_NR_VALUES) { 868 | memcpy(&response->value_list[response->value_list_length], value, sizeof(*value)); 869 | response->value_list_length++; 870 | memcpy(&oid_list[i], &value->oid, sizeof(value->oid)); 871 | found_repeater++; 872 | continue; 873 | } 874 | 875 | logit(LOG_ERR, 0, "%s", msg); 876 | return -1; 877 | } 878 | 879 | if (found_repeater == 0) 880 | break; 881 | } 882 | 883 | return 0; 884 | } 885 | 886 | 887 | int snmp_packet_complete(const client_t *client) 888 | { 889 | int type; 890 | size_t pos = 0, len = 0; 891 | 892 | /* 893 | * The SNMP message must be at least have a header containing sequence, 894 | * version, community, sequence, request id, 2 integers, sequence, oid 895 | * and null value. 896 | */ 897 | if (client->size < 25) 898 | return 0; 899 | 900 | /* The SNMP message is enclosed in a sequence */ 901 | if (decode_len(client->packet, client->size, &pos, &type, &len) == -1) 902 | return -1; 903 | 904 | if (type != BER_TYPE_SEQUENCE || len < 1 || len > (client->size - pos)) { 905 | logit(LOG_DEBUG, 0, "Unexpected SNMP header type %02X length %zu", type, len); 906 | errno = EINVAL; 907 | return -1; 908 | } 909 | 910 | /* Return whether we received the whole packet */ 911 | return ((client->size - pos) == len) ? 1 : 0; 912 | } 913 | 914 | int snmp(client_t *client) 915 | { 916 | response_t response; 917 | request_t request; 918 | 919 | /* Setup request and response (other code only changes non-defaults) */ 920 | memset(&request, 0, sizeof(request)); 921 | memset(&response, 0, sizeof(response)); 922 | 923 | /* Decode the request (only checks for syntax of the packet) */ 924 | if (decode_snmp_request(&request, client) == -1) 925 | return -1; 926 | 927 | /* 928 | * If we are using SNMP v2c or require authentication, check the community 929 | * string for length and validity. 930 | */ 931 | if (request.version == SNMP_VERSION_2C) { 932 | if (strcmp(g_community, request.community)) { 933 | response.error_status = (request.version == SNMP_VERSION_2C) ? SNMP_STATUS_NO_ACCESS : SNMP_STATUS_GEN_ERR; 934 | response.error_index = 0; 935 | goto done; 936 | } 937 | } else if (g_auth) { 938 | response.error_status = SNMP_STATUS_GEN_ERR; 939 | response.error_index = 0; 940 | goto done; 941 | } 942 | 943 | /* Now handle the SNMP requests depending on their type */ 944 | switch (request.type) { 945 | case BER_TYPE_SNMP_GET: 946 | if (handle_snmp_get(&request, &response, client) == -1) 947 | return -1; 948 | break; 949 | 950 | case BER_TYPE_SNMP_GETNEXT: 951 | if (handle_snmp_getnext(&request, &response, client) == -1) 952 | return -1; 953 | break; 954 | 955 | case BER_TYPE_SNMP_SET: 956 | if (handle_snmp_set(&request, &response, client) == -1) 957 | return -1; 958 | break; 959 | 960 | case BER_TYPE_SNMP_GETBULK: 961 | if (handle_snmp_getbulk(&request, &response, client) == -1) 962 | return -1; 963 | break; 964 | 965 | default: 966 | logit(LOG_ERR, 0, "UNHANDLED REQUEST TYPE %d", request.type); 967 | client->size = 0; 968 | return 0; 969 | } 970 | 971 | done: 972 | /* Encode the request (depending on error status and encode flags) */ 973 | if (encode_snmp_response(&request, &response, client) == -1) 974 | return -1; 975 | 976 | return 0; 977 | } 978 | 979 | #ifdef DEBUG 980 | int snmp_element_as_string(const data_t *data, char *buf, size_t size) 981 | { 982 | size_t i, len, pos = 0; 983 | int type, val; 984 | oid_t oid; 985 | unsigned int cnt; 986 | 987 | /* Decode the element type and length */ 988 | if (decode_len(data->buffer, data->encoded_length, &pos, &type, &len) == -1) 989 | return -1; 990 | 991 | /* Depending on type and length, decode the data */ 992 | switch (type) { 993 | case BER_TYPE_INTEGER: 994 | if (decode_int(data->buffer, data->encoded_length, &pos, len, &val) == -1) 995 | return -1; 996 | snprintf(buf, size, "%d", val); 997 | break; 998 | 999 | case BER_TYPE_OCTET_STRING: 1000 | snprintf(buf, size, "%.*s", (int)len, &data->buffer[pos]); 1001 | break; 1002 | 1003 | case BER_TYPE_OID: 1004 | if (decode_oid(data->buffer, data->encoded_length, &pos, len, &oid) == -1) 1005 | return -1; 1006 | snprintf(buf, size, "%s", oid_ntoa(&oid)); 1007 | break; 1008 | 1009 | case BER_TYPE_COUNTER: 1010 | case BER_TYPE_GAUGE: 1011 | case BER_TYPE_TIME_TICKS: 1012 | if (decode_cnt(data->buffer, data->encoded_length, &pos, len, &cnt) == -1) 1013 | return -1; 1014 | snprintf(buf, size, "%u", cnt); 1015 | break; 1016 | 1017 | case BER_TYPE_NO_SUCH_OBJECT: 1018 | snprintf(buf, size, "noSuchObject"); 1019 | break; 1020 | 1021 | case BER_TYPE_NO_SUCH_INSTANCE: 1022 | snprintf(buf, size, "noSuchInstance"); 1023 | break; 1024 | 1025 | case BER_TYPE_END_OF_MIB_VIEW: 1026 | snprintf(buf, size, "endOfMibView"); 1027 | break; 1028 | 1029 | default: 1030 | for (i = 0; i < len && i < ((size - 1) / 3); i++) 1031 | snprintf(buf + 3 * i, 4, "%02X ", data->buffer[pos + i]); 1032 | 1033 | if (len > 0) 1034 | buf[len * 3 - 1] = '\0'; 1035 | else 1036 | buf[0] = '\0'; 1037 | break; 1038 | } 1039 | 1040 | return 0; 1041 | } 1042 | #endif /* DEBUG */ 1043 | 1044 | /* vim: ts=4 sts=4 sw=4 nowrap 1045 | */ 1046 | -------------------------------------------------------------------------------- /utils.c: -------------------------------------------------------------------------------- 1 | /* Utility functions 2 | * 3 | * Copyright (C) 2008-2010 Robert Ernst 4 | * Copyright (C) 2015-2020 Joachim Nilsson 5 | * 6 | * This file may be distributed and/or modified under the terms of the 7 | * GNU General Public License version 2 as published by the Free Software 8 | * Foundation and appearing in the file LICENSE.GPL included in the 9 | * packaging of this file. 10 | * 11 | * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE 12 | * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 13 | * 14 | * See COPYING for GPL licensing information. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #ifdef HAVE_ALLOCA_H 22 | #include 23 | #endif 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include "mini-snmpd.h" 34 | 35 | void *allocate(size_t len) 36 | { 37 | char *buf = malloc(len); 38 | 39 | if (!buf) { 40 | logit(LOG_DEBUG, errno, "Failed allocating memory"); 41 | return NULL; 42 | } 43 | 44 | return buf; 45 | } 46 | 47 | 48 | static inline int parse_lineint(char *buf, field_t *f, size_t *skip_prefix) 49 | { 50 | char *ptr, *prefixptr; 51 | size_t i; 52 | 53 | ptr = buf; 54 | while (isspace(*ptr)) 55 | ptr++; 56 | if (!*ptr) 57 | return 0; 58 | 59 | /* Check if buffer begins with prefix */ 60 | prefixptr = f->prefix; 61 | while (*prefixptr) { 62 | if (*prefixptr++ != *ptr++) 63 | return 0; 64 | } 65 | if (*ptr == ':') /* Prefix may have a ':', skip it too! */ 66 | ptr++; 67 | else if (!isspace(*ptr))/* If there is NO ':' after prefix there must be a space, otherwise we got a partial match */ 68 | return 0; 69 | 70 | if (skip_prefix != NULL) { 71 | if (*skip_prefix > 0) { 72 | (*skip_prefix)--; 73 | return 0; 74 | } 75 | } 76 | 77 | for (i = 0; i < f->len; i++) { 78 | while (isspace(*ptr)) 79 | ptr++; 80 | 81 | if (f->value[i]) { 82 | *(f->value[i]) = strtoll(ptr, NULL, 0); 83 | } 84 | 85 | while (!isspace(*ptr)) 86 | ptr++; 87 | } 88 | 89 | return 1; 90 | } 91 | 92 | int parse_file(char *file, field_t fields[], size_t limit, size_t skip_prefix) 93 | { 94 | char buf[512]; 95 | FILE *fp; 96 | 97 | if (!file || !fields) 98 | return -1; 99 | 100 | fp = fopen(file, "r"); 101 | if (!fp) 102 | return -1; 103 | 104 | while (fgets(buf, sizeof(buf), fp)) { 105 | size_t i; 106 | 107 | for (i = 0; i < limit; i++) { 108 | if (!fields[i].prefix) 109 | continue; 110 | 111 | if (parse_lineint(buf, &fields[i], &skip_prefix)) 112 | break; 113 | } 114 | } 115 | 116 | return fclose(fp); 117 | } 118 | 119 | int read_file(const char *filename, char *buf, size_t size) 120 | { 121 | int ret; 122 | FILE *fp; 123 | size_t len; 124 | 125 | fp = fopen(filename, "r"); 126 | if (!fp) { 127 | logit(LOG_WARNING, errno, "Failed opening %s", filename); 128 | return -1; 129 | } 130 | 131 | len = fread(buf, 1, size - 1, fp); 132 | ret = fclose(fp); 133 | if (len == 0 || ret == -1) { 134 | logit(LOG_WARNING, errno, "Failed reading %s", filename); 135 | return -1; 136 | } 137 | 138 | buf[len] = '\0'; 139 | 140 | return 0; 141 | } 142 | 143 | unsigned int read_value(const char *buf, const char *prefix) 144 | { 145 | buf = strstr(buf, prefix); 146 | if (!buf) 147 | return 0; 148 | 149 | buf += strlen(prefix); 150 | if (*buf == ':') 151 | buf++; 152 | 153 | while (isspace(*buf)) 154 | buf++; 155 | 156 | return (*buf != 0) ? strtoul(buf, NULL, 0) : 0; 157 | } 158 | 159 | void read_values(const char *buf, const char *prefix, unsigned int *values, int count) 160 | { 161 | int i; 162 | 163 | buf = strstr(buf, prefix); 164 | if (!buf) { 165 | memset(values, 0, count * sizeof(unsigned int)); 166 | return; 167 | } 168 | 169 | buf += strlen(prefix); 170 | if (*buf == ':') 171 | buf++; 172 | 173 | for (i = 0; i < count; i++) { 174 | while (isspace(*buf)) 175 | buf++; 176 | 177 | if (*buf == 0) { 178 | values[i] = 0; 179 | continue; 180 | } 181 | 182 | values[i] = strtoul(buf, (char **)&buf, 0); 183 | } 184 | } 185 | 186 | /* For files like Linux /sys/class/net/lo/mtu */ 187 | int read_file_value(unsigned int *val, const char *fmt, ...) 188 | { 189 | va_list ap; 190 | FILE *fp; 191 | char buf[256]; 192 | int rc = -1; 193 | 194 | va_start(ap, fmt); 195 | vsnprintf(buf, sizeof(buf), fmt, ap); 196 | va_end(ap); 197 | 198 | fp = fopen(buf, "r"); 199 | if (fp) { 200 | if (fgets(buf, sizeof(buf), fp)) { 201 | *val = strtoul(buf, NULL, 0); 202 | rc = 0; 203 | } 204 | 205 | fclose(fp); 206 | } 207 | 208 | return rc; 209 | } 210 | 211 | int ticks_since(const struct timeval *tv_last, struct timeval *tv_now) 212 | { 213 | float ticks; 214 | 215 | if (gettimeofday(tv_now, NULL) == -1) { 216 | logit(LOG_WARNING, errno, "could not get ticks"); 217 | return -1; 218 | } 219 | 220 | if (tv_now->tv_sec < tv_last->tv_sec || (tv_now->tv_sec == tv_last->tv_sec && tv_now->tv_usec < tv_last->tv_usec)) { 221 | logit(LOG_WARNING, 0, "could not get ticks: time running backwards"); 222 | return -1; 223 | } 224 | 225 | ticks = (float)(tv_now->tv_sec - 1 - tv_last->tv_sec) * 100.0 + (float)((tv_now->tv_usec + 1000000 - tv_last->tv_usec) / 10000); 226 | #ifdef DEBUG 227 | logit(LOG_DEBUG, 0, "seconds since last update: %.2f", ticks / 100); 228 | #endif 229 | if (ticks < INT_MIN) 230 | return INT_MIN; 231 | if (ticks > INT_MAX) 232 | return INT_MAX; 233 | 234 | return ticks; 235 | } 236 | 237 | #ifdef DEBUG 238 | void dump_packet(const client_t *client) 239 | { 240 | size_t i, len = 0; 241 | char *buf = allocate(BUFSIZ); 242 | char straddr[my_inet_addrstrlen]; 243 | my_in_addr_t client_addr; 244 | 245 | if (!buf) 246 | return; 247 | 248 | client_addr = client->addr; 249 | for (i = 0; i < client->size; i++) { 250 | len += snprintf(buf + len, BUFSIZ - len, i ? " %02X" : "%02X", client->packet[i]); 251 | if (len >= BUFSIZ) 252 | break; 253 | } 254 | 255 | inet_ntop(my_af_inet, &client_addr, straddr, sizeof(straddr)); 256 | logit(LOG_DEBUG, 0, "%s %u bytes %s %s:%d (%s)", 257 | client->outgoing ? "transmitted" : "received", (int) client->size, 258 | client->outgoing ? "to" : "from", straddr, 259 | ntohs(client->port), buf); 260 | 261 | free(buf); 262 | } 263 | 264 | void dump_mib(const value_t *value, int size) 265 | { 266 | int i; 267 | char *buf = allocate(BUFSIZ); 268 | 269 | if (!buf) 270 | return; 271 | 272 | for (i = 0; i < size; i++) { 273 | if (snmp_element_as_string(&value[i].data, buf, BUFSIZ) == -1) 274 | strncpy(buf, "?", BUFSIZ); 275 | 276 | logit(LOG_DEBUG, 0, "mib entry[%d]: oid='%s', max_length=%zu, data='%s'", 277 | i, oid_ntoa(&value[i].oid), value[i].data.max_length, buf); 278 | } 279 | 280 | free(buf); 281 | } 282 | 283 | void dump_response(const response_t *response) 284 | { 285 | size_t i; 286 | char *buf = allocate(MAX_PACKET_SIZE); 287 | 288 | if (!buf) 289 | return; 290 | 291 | logit(LOG_DEBUG, 0, "response: status=%d, index=%d, nr_entries=%zu", 292 | response->error_status, response->error_index, response->value_list_length); 293 | for (i = 0; i < response->value_list_length; i++) { 294 | if (snmp_element_as_string(&response->value_list[i].data, buf, MAX_PACKET_SIZE) == -1) 295 | strncpy(buf, "?", MAX_PACKET_SIZE); 296 | 297 | logit(LOG_DEBUG, 0, "response: entry[%zu]='%s','%s'", 298 | i, oid_ntoa(&response->value_list[i].oid), buf); 299 | } 300 | 301 | free(buf); 302 | } 303 | #endif /* DEBUG */ 304 | 305 | char *oid_ntoa(const oid_t *oid) 306 | { 307 | size_t i, len = 0; 308 | static char buf[MAX_NR_SUBIDS * 10 + 2]; 309 | 310 | buf[0] = '\0'; 311 | for (i = 0; i < oid->subid_list_length; i++) { 312 | len += snprintf(buf + len, sizeof(buf) - len, ".%u", oid->subid_list[i]); 313 | if (len >= sizeof(buf)) 314 | break; 315 | } 316 | 317 | return buf; 318 | } 319 | 320 | oid_t *oid_aton(const char *str) 321 | { 322 | static oid_t oid; 323 | char *ptr = (char *)str; 324 | 325 | if (!str) 326 | return NULL; 327 | 328 | oid.subid_list_length = 0; 329 | while (*ptr != 0) { 330 | if (oid.subid_list_length >= MAX_NR_SUBIDS) 331 | return NULL; 332 | 333 | if (*ptr != '.') 334 | return NULL; 335 | 336 | ptr++; 337 | if (*ptr == 0) 338 | return NULL; 339 | 340 | oid.subid_list[oid.subid_list_length++] = strtoul(ptr, &ptr, 0); 341 | } 342 | 343 | if (oid.subid_list_length < 2 || (oid.subid_list[0] * 40 + oid.subid_list[1]) > 0xFF) 344 | return NULL; 345 | 346 | return &oid; 347 | } 348 | 349 | int oid_cmp(const oid_t *oid1, const oid_t *oid2) 350 | { 351 | int subid1, subid2; 352 | size_t i; 353 | 354 | for (i = 0; i < MAX_NR_OIDS; i++) { 355 | subid1 = (oid1->subid_list_length > i) ? (int)oid1->subid_list[i] : -1; 356 | subid2 = (oid2->subid_list_length > i) ? (int)oid2->subid_list[i] : -1; 357 | 358 | if (subid1 == -1 && subid2 == -1) 359 | return 0; 360 | if (subid1 > subid2) 361 | return 1; 362 | if (subid1 < subid2) 363 | return -1; 364 | } 365 | 366 | return 0; 367 | } 368 | 369 | int split(const char *str, char *delim, char **list, int max_list_length) 370 | { 371 | int len = 0; 372 | char *ptr; 373 | char *buf = strdup(str); 374 | 375 | if (!buf) 376 | return 0; 377 | 378 | for (ptr = strtok(buf, delim); ptr; ptr = strtok(NULL, delim)) { 379 | if (len < max_list_length) 380 | list[len++] = strdup(ptr); 381 | } 382 | 383 | free(buf); 384 | 385 | return len; 386 | } 387 | 388 | client_t *find_oldest_client(void) 389 | { 390 | size_t i, found = 0, pos = 0; 391 | time_t timestamp = (time_t)LONG_MAX; 392 | 393 | for (i = 0; i < g_tcp_client_list_length; i++) { 394 | if (timestamp > g_tcp_client_list[i]->timestamp) { 395 | timestamp = g_tcp_client_list[i]->timestamp; 396 | found = 1; 397 | pos = i; 398 | } 399 | } 400 | 401 | return found ? g_tcp_client_list[pos] : NULL; 402 | } 403 | 404 | int find_ifname(char *ifname) 405 | { 406 | int i; 407 | 408 | for (i = 0; i < (int)g_interface_list_length; i++) { 409 | if (!strcmp(g_interface_list[i], ifname)) 410 | return i; 411 | } 412 | 413 | return -1; 414 | } 415 | 416 | #ifdef CONFIG_ENABLE_DEMO 417 | void get_demoinfo(demoinfo_t *demoinfo) 418 | { 419 | static int did_init = 0; 420 | 421 | if (did_init == 0) { 422 | srand(time(NULL)); 423 | did_init = 1; 424 | } 425 | 426 | demoinfo->random_value_1 = rand(); 427 | demoinfo->random_value_2 = rand(); 428 | } 429 | #endif 430 | 431 | int logit(int priority, int syserr, const char *fmt, ...) 432 | { 433 | va_list ap; 434 | char *buf; 435 | int len, i; 436 | 437 | if (LOG_PRI(priority) > g_level) 438 | return 0; 439 | 440 | va_start(ap, fmt); 441 | len = vsnprintf(NULL, 0, fmt, ap); 442 | va_end(ap); 443 | if (len < 0) 444 | return -1; 445 | 446 | /* length of ": error-message" */ 447 | len += 3 + (syserr > 0 ? strlen(strerror(syserr)) : 0); 448 | buf = alloca(len); 449 | if (!buf) 450 | return -1; 451 | 452 | va_start(ap, fmt); 453 | i = vsnprintf(buf, len, fmt, ap); 454 | va_end(ap); 455 | if (i < 0) 456 | return -1; 457 | 458 | if (syserr > 0) 459 | i += snprintf(&buf[i], len - i, ": %s", strerror(syserr)); 460 | 461 | if (g_syslog) 462 | syslog(priority, "%s", buf); 463 | else 464 | i = fprintf(stderr, "%s\n", buf); 465 | 466 | return i; 467 | } 468 | 469 | /* vim: ts=4 sts=4 sw=4 nowrap 470 | */ 471 | --------------------------------------------------------------------------------