From ce019318f4048c96ab28ffcb0312cb752b4f62ed Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 5 May 2024 19:40:16 +0200 Subject: Adding upstream version 1:2.1.2. Signed-off-by: Daniel Baumann --- COPYING | 339 +++++++++ COPYING.LIB | 502 ++++++++++++++ CREDITS | 27 + ChangeLog | 448 ++++++++++++ Make.defines | 55 ++ Make.rules | 213 ++++++ Makefile | 117 ++++ README | 39 ++ TODO | 33 + VERSION | 1 + chvers.sh | 102 +++ default.rules | 193 ++++++ include/version.h | 9 + libsupp/clif.c | 1259 +++++++++++++++++++++++++++++++++ libsupp/clif.h | 121 ++++ store.sh | 72 ++ traceroute.spec | 59 ++ traceroute/as_lookups.c | 128 ++++ traceroute/csum.c | 34 + traceroute/extension.c | 132 ++++ traceroute/flowlabel.h | 40 ++ traceroute/mod-dccp.c | 289 ++++++++ traceroute/mod-icmp.c | 247 +++++++ traceroute/mod-raw.c | 163 +++++ traceroute/mod-tcp.c | 507 ++++++++++++++ traceroute/mod-tcpconn.c | 228 ++++++ traceroute/mod-udp.c | 234 +++++++ traceroute/module.c | 35 + traceroute/poll.c | 88 +++ traceroute/random.c | 28 + traceroute/time.c | 27 + traceroute/traceroute.8 | 596 ++++++++++++++++ traceroute/traceroute.c | 1693 +++++++++++++++++++++++++++++++++++++++++++++ traceroute/traceroute.h | 107 +++ wrappers/Makefile | 15 + wrappers/README.wrappers | 18 + wrappers/lft | 117 ++++ wrappers/tcptraceroute | 78 +++ wrappers/tcptraceroute.8 | 34 + wrappers/tracepath | 63 ++ wrappers/traceproto | 127 ++++ wrappers/traceroute-nanog | 76 ++ 42 files changed, 8693 insertions(+) create mode 100644 COPYING create mode 100644 COPYING.LIB create mode 100644 CREDITS create mode 100644 ChangeLog create mode 100644 Make.defines create mode 100644 Make.rules create mode 100644 Makefile create mode 100644 README create mode 100644 TODO create mode 100644 VERSION create mode 100755 chvers.sh create mode 100644 default.rules create mode 100644 include/version.h create mode 100644 libsupp/clif.c create mode 100644 libsupp/clif.h create mode 100755 store.sh create mode 100644 traceroute.spec create mode 100644 traceroute/as_lookups.c create mode 100644 traceroute/csum.c create mode 100644 traceroute/extension.c create mode 100644 traceroute/flowlabel.h create mode 100644 traceroute/mod-dccp.c create mode 100644 traceroute/mod-icmp.c create mode 100644 traceroute/mod-raw.c create mode 100644 traceroute/mod-tcp.c create mode 100644 traceroute/mod-tcpconn.c create mode 100644 traceroute/mod-udp.c create mode 100644 traceroute/module.c create mode 100644 traceroute/poll.c create mode 100644 traceroute/random.c create mode 100644 traceroute/time.c create mode 100644 traceroute/traceroute.8 create mode 100644 traceroute/traceroute.c create mode 100644 traceroute/traceroute.h create mode 100644 wrappers/Makefile create mode 100644 wrappers/README.wrappers create mode 100755 wrappers/lft create mode 100755 wrappers/tcptraceroute create mode 100644 wrappers/tcptraceroute.8 create mode 100755 wrappers/tracepath create mode 100755 wrappers/traceproto create mode 100755 wrappers/traceroute-nanog diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/COPYING.LIB b/COPYING.LIB new file mode 100644 index 0000000..4362b49 --- /dev/null +++ b/COPYING.LIB @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/CREDITS b/CREDITS new file mode 100644 index 0000000..3e53d73 --- /dev/null +++ b/CREDITS @@ -0,0 +1,27 @@ +Thanks for the testing, proposals, bug fixing: + +Robert Scheck (redhat-bugzilla@linuxnetz.de) +Peter Bieringer (pb@bieringer.de) +Martin Bacovsky (mbacovsk@redhat.com) +Andy Shevchenko (andriy@asplinux.ru) +Mike Frysinger (vapier@gentoo.org) +Chris Ward (cward@redhat.com) +Teran McKinney (sega01@gmail.com) +Kaj Niemi (kajtzu@a51.org) +Milos Malik (mmalik@redhat.com) +Andreas Mohr (andim2@users.sourceforge.net) +Vladz (vladz@devzero.fr) +Daniel Baumann (daniel@debian.org) +Filip Holec (fholec@redhat.com) +Samuel Jero (sj323707@ohio.edu) +Jan Synacek (jsynacek@redhat.com) +Frederic Mangano (fmang@mg0.fr) +Jeff (geogriffin@jsgriff.com) +Andrew Schwartz (schwartz@amacapital.net) +Felix Janda (felix.janda@posteo.de) +Sergey Salnikov (serg@salnikov.ru) +Richard Sheehan (richardsheehan@users.sourceforge.net) +Eric Dumazet (edumazet@google.com) +... +maybe you too? ;) + diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..0c7c6d0 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,448 @@ +2023-02-13 Dmitry Butskoy - 2.1.2 + + * Fix unprivileged ICMP tracerouting with Linux kernel >= 6.1 + (Eric Dumazet, SF bug #14) + +2022-12-27 Dmitry Butskoy - 2.1.1 + + * Interpret ipv4-mapped ipv6 addresses (::ffff:A.B.C.D) as true ipv4. + + There are no ipv4-mapped addresses in the real network which we + operate on, so use just ipv4 in such cases, but allow users + to specify it this way for convenience. + + * Return back more robast poll(2) loop handling. + + +2016-03-08 Dmitry Butskoy - 2.1.0 + + * Improve the main loop for better interactivity. + + Instead of waiting silently for maximum expiration time of probes + in progress, use timeout of the first probe (which will be printed + first from now) only. + + * Speedup wait mechanism. + + Traditional traceroute implementation always waited the whole timeout + for any probe. But if we already have some replies from the same hop, + or even from some next hop, we can use the round trip time + of such a reply as a hint to determine the actual reasonable + amount of time to wait. + + Now the `-w' option has a form of three (in general) float values + separated by a comma (or a slash): `-w MAX_SECS,HERE,NEAR' . + (last two are optional). MAX_SECS specifies the maximum time + (in seconds) to wait, in any case. + + The optional HERE specifies a factor to multiply the round trip time + of an already received response from the same hop. + The resulting value is used as a timeout for the probe, instead of + (but no more than) MAX_SECS. The optional NEAR specifies a similar + factor for a response from some next hop. + The time of the first found result is used in both cases. + + First, we look for the same hop (of the probe which will be printed + first from now). If nothing found, then look for some next hop. + If nothing found, use MAX_SECS. If HERE and/or NEAR have zero values, + the corresponding computation is skipped. + + HERE and NEAR are always set to zero if only MAX_SECS is specified + (which provides compatibility with previous versions). Thus, if your + scripts use `-w SECS', then nothing changed for you, since + the lonely SECS implies `-w SECS,0,0' . + + Defaults are 5.0 seconds for MAX_SECS, 3.0 times for HERE and + 10.0 times for NEAR. + + Certainly, the new algorithm can lead to premature expiry + (especially when response times differ at times) and printing "*" + instead of a time. Anyway, you can always switch this algorithm off, + just by specifying `-w' with the desired timeout only (fe. `-w 5'). + + We continue to wait whole MAX_SECS when one probe per time + must be sent (`--sport', `-P proto'), because it seems more harmful + rather than helpful to try to wait less in such cases. + + To provide compatibility with 2.0.x versions, use: + + traceroute -w 5 + + (or any other desired `-w' value). + + * Hint people to use the system traceroute(8) instead of + tcptraceroute wrapper (by providing a stderr header). + + The using of this wrapper is a little bit harmful, since it has + less possibilities and a little different set of options. + + For those who are used to use tcptraceroute in cmdline, + just create a link with that name to the system traceroute. + When invoked as "tcp*", it then behaves as `traceroute -T'. + (The simple manual page added for this case in the wrapper subdir). + + The original tcptraceroute had some options differ ("lpNSAE"), + but they was rare used. Most common "dnFifmqwst" was just the same. + Therefore it should be painless to use the system binary directly, + instead of the limited wrapper (which is still provided indeed). + + +2016-02-15 Dmitry Butskoy - 2.0.22 + + * Some portability fixing and improvements (Felix Janda) + + * Require clear numbers for options and arguments (Sergey Salnikov) + + * Drop compilation date from the version string (Debian #774365) + + * New tcp module option `reuse', which utilize SO_REUSEADDR + to reuse local port numbers for the huge workloads (Richard Sheehan) + + * Avoid poll(2) call with spurious zero timeout in some rare cases + by rounding the value properly using ceil(3) + + +2014-11-12 Dmitry Butskoy - 2.0.21 + + * Fix `--mtu' and `-F' working on kernels >= 3.13 + + * Some manual page improving (Christopher Mann) + + +2014-06-14 Dmitry Butskoy - 2.0.20 + + * Describe all complementary long options in the man page (Jan Synacek) + + * Use correct service name for AS lookups (Frederic Mangano) + + * Avoid some rare case null dereference (geogriffin@jsgriff.com) + + * Improve expiration check for simultaneous probes + + +2012-11-19 Dmitry Butskoy - 2.0.19 + + * DCCP protocol support (rfc4340), by Samuel Jero + + Use "-D" option for it (the protocol-specific options + are available too). + + * Update COPYING and COPYING.LIB license files to the latest + published ones (due to FSF address changes etc.) (Jan Synacek) + + * Add mention of "-l" option to manual (Filip Holec) + + +2011-08-16 Dmitry Butskoy - 2.0.18 + + * Handle new dgram icmp sockets ("echo ping sockets"), + appeared in kernel 3.0 . + + Now unprivileged users may perform ICMP tracerouting + without any special rights of the executable + (neither setuid bits nor cap_net_raw settings). + It is allowed if any group of a user matches sysctl range + of "net/ipv4/ping_group_range". + + The support for dgram icmp way (and whether it is allowed) + is auto-detected at runtime. First, the traditional raw socket + is tried (for full compatibility reasons), then new dgram + socket as a fallback. + The icmp module now has two additional options "raw" and "dgram", + which cause to try one particular way only. + + Note, that there is no IPv6 implementation for dgram icmp sockets + in kernels 3.0 yet, but new traceroute is ready for it anyway. + + * New tcp module option `info' ("-T -O info"), + which prints all tcp flags of tcp reply from the reached + target host. + + The flags are shown comma-separated in the same place + where icmp extensions is printed (ie. in `<>' brackets) + + This feature is utilized by tcptraceroute wrapper now, + and allow it to be completely functional replacement + of the original tcptraceroute. + + * Fix determination of system-wide ECN setings for tcp module. + + Since the kernel 2.6.31 the default sysctl net/ipv4/tcp_ecn + was changed from zero to '2', whereas the actual value + for ecn to be set is still '1' + + * Allow different packet sizes for `--mtu'. + Suport `-l' option for tracepath wrapper. + + * Some code and manual cleanups + + +2010-12-14 Dmitry Butskoy - 2.0.17 + + * Adapt code to make possible the use of Linux capabilities + (for raw sockets etc.) instead of superuser privileges only. + + On modern systems the capabilities can be stored as + file attributes, ie.: + + "setcap cap_net_raw=pe /usr/bin/traceroute" + + +2010-09-13 Dmitry Butskoy - 2.0.16 + + * A little work-around in the build system + for the new (buggy?) make 3.82 + + * Add `--fwmark=num' option for firewall mark (for kernel >= 2.6.25). + Idea comes from an anonymous SF patch #3042539 + + +2010-07-14 Dmitry Butskoy - 2.0.15 + + * Use string routines more safely (fix SF bug #3029216) + + * Provide help for lft wrapper + + +2010-04-21 Dmitry Butskoy - 2.0.14 + + * Fix support for IPv6's flow_labels and tclass. + Thanks to Peter Bieringer for testing + + * Use route header "type 2" instead of deprecated "type 0" + for `-g' option for IPv6. The default value can be changed + by specifying a number in the place of the first `-g' address. + + +2009-11-02 Dmitry Butskoy - 2.0.13 + + * Check for first_hop is not zero value (vladz@devzero.fr) + + * Always fill unresolved IP address by its numeric interpretation, + even if getnameinfo(3) leaves it untouched (as it does for ipv6 + in some glibc versions, whereas always fills for ipv4) + + * Cosmetic changes for man page (Andreas Mohr) + + +2008-09-15 Dmitry Butskoy - 2.0.12 + + * Use common recv_reply() routine for all modules which + do recvmsg(2) call. Method-specific things go to callbacks. + + Pass to init methods pointer to datalen instead of the value. + + * Implement ICMP Extension support (rfc4884), `-e' option. + Parse MPLS info (rfc4950) to be more readable (Kaj Niemi) + + * Implement Path MTU Discovery (similar to tracepath(1)), + with `--mtu' option. Changed mtu is printed once in a form + of `F=NUM' at the first probe of a hop which requires + such mtu to be reached. (Actually, the correspond "frag needed" + icmp message is normally sent by the previous hop). + + * Print the number of backward hops when it differs with forward, + by `--back' option. The backward hops is guessed by a technique + similar to tracepath(1), there is no reliable way to obtain + such info though. + + * The optional second argument (packet_len) now is the full length + of the packet, including IP headers. (It is obvious enough due to + the nature of this feature, and this is the behaviour of the + original traceroute). Particular trace methods can ignore this + (fe. tcp), or increase it up to the minimal value (udp, icmp). + The actual packet's size is alvays reported in the output header. + + * Add tracepath(1)/tracepath6(1) shell wrapper. + + * Allow DEF_AF to be redefined at cmdline (Teran McKinney) + + * Do not check the correctness of `sim_probes' value -- it is + unneeded at all. This also fixes a bug when a value of sim_probes + appears to be more than the total number of probes. + Reported by Milos Malik. + + * Allow default UDP method to cross zero port boundary (Milos Malik). + It is a strange corner case, but traditional traceroute + behaves exactly so. + + +2008-04-25 Dmitry Butskoy - 2.0.11 + + * Use new pmtudisc value "probe" instead of "do" for `-F' option + (available since the kernel 2.6.22). + + For kernels before 2.6.22, the `-F' (dontfragment) option + seems completely useless for IPv6 and partially useful + for IPv4 (when a user can flush routing caches some way). + + * Fix installation in build system (Mike Frysinger) + + * Don't compute checksum for ipv6 icmp packets ourselves, + the kernel overwrites it anyway by the proper values. + + * Don't use explicit path to traceroute in wrapper scripts + + +2008-04-17 Dmitry Butskoy - 2.0.10 + + * raw_can_connect(): ipv6 connected raw sockets + receive MSG_ERRQUEUE properly only for kernels >= 2.6.25 + + * remove useless "host" parameter for init methods + + * add probe_by_seq() and probe_by_sk() routines, + don't pass whole probes' pointer to recv_probe method + + * collect all sends in do_send() routine + + * Interpret ENOBUFS errors for send(2) as "can retry later". + + Slow devices (like ppp) with small tx_queue_len can reject + the sending of too many packets simultaneously. To handle this, + do_send() now returns a negate value in a case of ENOBUFS + and similar (instead of program exit). The send_probe method + clears the probe and returns immediately in such cases. + Then, if there is an amount of time to wait for something, + the failed probe will be attempted again after that time expired. + If nothing to wait more, the program is exited. + + +2007-09-26 Dmitry Butskoy - 2.0.9 + + * Complete manual page. + + * Edit manual page to sound more English, thanks to Chris Ward + + +2007-09-04 Dmitry Butskoy - 2.0.8 + + * Move all wrappers to special "wrappers/" dir. + Add lft(8) shell wrapper. + Add traceproto(8) shell wrapper. + Add traceroute-nanog(8) shell wrapper. + + * Interpret first_hop as number, not index + + * Build system is re-worked to match more the modern requirements + (Thanks to Mike Frysinger for testing). + + * Check for kernel version >= 2.6.22.2 in raw_can_connect() + + * Add generic "raw" method, "-P protonum" option. + New "one_per_time" flag for tr_module. + + +2007-07-31 Dmitry Butskoy - 2.0.7 + + * Fix revents checking typo + + * Expect normal data reply from udp too. + + * Implement udp to port (-U) and udplite (-UL) methods. + Both available for unprivileged users. + Add "coverage" option for udplite. + + * Allow non-digit service names for `-p' and `--sport' + + * Drop period at the end of "SEE ALSO" section, and + avoid specific distro names in the manual (Mike Frysinger) + + * Explicitly mention that this program is licensed + as "GPL version 2 or any later version". + (Similar for libsupp subdir: LGPL version 2.1 or any later). + + * Always check whether the dest and source port match in + received packets. Can decrease an amount of (hypothetical) + garbage received just after the bind() but before connect() + + +2007-07-19 Dmitry Butskoy - 2.0.6 + + * Rename tr_ops to tr_module + + * Implement module-specific options (-O opt,...) + + * Add TCP specific options (all the tcp header flags, + ecn, sack, timestamps, window_scaling, mss, sysctl) + Build tcp probe packet depending on them. + + * Add "--sport" option for explicit source port selection. + Always cause "-N 1" when it is set. + + * Add new routine bind_socket(). + Always (auto)bind sockets in tune_socket(). + + * Add tcptraceroute(8) shell wrapper + + +2007-07-16 Dmitry Butskoy - 2.0.5 + + * Use MSG_ERRQUEUE for raw sockets too. + + * raw_can_connect () work-around for kernel bug #8747 + + * random.c, csum.c: new separate files + + * New implementation of tcp method ("-T"), using + half-open technique. The old implementation module + renamed to "tcpconn" ("-M tcpconn"). + + * Common parse_cmsg() routine + + * put ee_info for parse_icmp_res() too, + handle ICMP6_PACKET_TOO_BIG for IPv6, + report "!F-num" when "frag needed" (legacy compatibility) + + +2007-07-11 Dmitry Butskoy - 2.0.4 + + * clear includes of unneeded headers + + * move poll stuff to separate poll.c + + * add module stuff (module.c), options etc. + Adapt udp/icmp/tcp for this. + + * Add common routines use_recverr() and set_ttl() + + +2007-02-28 Dmitry Butskoy + + * fix variable type for getsockname (Mike Frysinger) + + +2007-01-09 Dmitry Butskoy - 2.0.3 + + * version 2.0.3 + + * allow option args without separator (add CLIF_MAY_JOIN_ARG flag), + for compatibility (Benjamin LaHaise) + + * no more "tcptraceroute" symlink for rpm packages, because + it conflicts with the same-name old package anyway (James Ralston) + + * fix compilation on glibc < 2.4 (Andy Shevchenko) + + +2006-10-30 Dmitry Butskoy - 2.0.2 + + * version 2.0.2 + + * More accurate check_expired() routine. + + * Some minor fixes. + + * Add NOTES section to manual + + +2006-10-20 Dmitry Butskoy - 2.0.1 + + * version 2.0.1 + + * Now ops methods write send_time (as well as recv_time) + + * Use SO_TIMESTAMP to obtain msecs precisely + + * Complete manual + diff --git a/Make.defines b/Make.defines new file mode 100644 index 0000000..185db54 --- /dev/null +++ b/Make.defines @@ -0,0 +1,55 @@ +# The name of this distributive (used for tarballs etc.). +# *MUST* be defined here... +# +NAME = traceroute + +# All subdir lists below are space-separated, with make's '%' wildcard +# allowed. Default value is shown in commented out example. +# + +# Subdirs to exclude anyway +# +#SKIPDIRS = tmp% + +# Subdirs to be treated as libdirs. +# +#LIBDIRS = lib% + +# An exact non-wildcard list of libdirs to be placed last in LIBDIRS list, +# with the order specified. Useful, when some library should be +# specified last because of referencing from another library... +# +#LIBLAST = + +# Subdirs to be treated as includedirs. +# Also all LIBDIRS will be actually used for includes too. +# +#INCLUDEDIRS = include% + +# Subdirs to be treated as moduledirs. +# +#MODDIRS = mod% + +# Subdirs of executables which will use modules at run-time linking. +# +#MODUSERS = + +# Subdirs of executables to be installed in `*/sbin' instead of `*/bin'. +# +#SBINUSERS = + +# Subdirs to exclude from install +# +SKIPINSTALL = test% libsupp + + +# A better way to alter the variables is to use "+=" or +# "override VAR += value" +# +# CFLAGS += +# CPPFLAGS += +# LDFLAGS += +# LIBS += + +CPPFLAGS += -D_GNU_SOURCE + diff --git a/Make.rules b/Make.rules new file mode 100644 index 0000000..ce5b033 --- /dev/null +++ b/Make.rules @@ -0,0 +1,213 @@ +# +# Copyright (c) 2000, 2001, 2007 Dmitry Butskoy +# +# License: GPL v2 or any later +# +# See COPYING for the status of this software. +# + +# +# Common rule and variable definitions. +# This file should be included by main and by per-target Makefiles. +# + + +ifndef srcdir +$(error srcdir variable not defined) +endif + + +MAKE = make --no-print-directory -r + + +# Use env=yes on cmdline to inherit environment values + +ifeq ($(env),yes) +define set +$(eval ifneq ($$(origin $(1)),environment) +$(1) = $(2) +else +override MAKE := $(1)="$($(strip $(1)))" $(MAKE) +endif) +endef +else +set = $(eval $(1) = $(2)) +endif + + +$(call set, CROSS, ) +$(call set, CC, $$(CROSS)gcc) +$(call set, AS, $$(CROSS)as) +$(call set, LD, $$(CROSS)ld) +$(call set, DEPEND, $$(CROSS)gcc -MM -MG) +$(call set, AR, $$(CROSS)ar) +$(call set, RANLIB, $$(CROSS)ranlib) +$(call set, INSTALL, cp) +$(call set, INDENT, true) + +gcc = $(findstring gcc,$(CC)) +$(call set, CFLAGS, $(if $(gcc), -O2 -Wall, -O)) +$(call set, CPPFLAGS, ) +$(call set, LDFLAGS, -s) +$(call set, LIBS, ) + + +# install stuff +prefix = /usr/local + +ifneq ($(wildcard /lib64/libc.* /usr/lib64/libc.*),) +lib := lib64 +else +lib := lib +endif + +exec_prefix = $(prefix) +bindir = $(exec_prefix)/bin +sbindir = $(exec_prefix)/sbin +libdir = $(exec_prefix)/$(lib) +libexecdir = $(exec_prefix)/libexec/$(NAME) +sysconfdir = $(prefix)/etc +includedir = $(prefix)/include +datadir = $(prefix)/share +mandir = $(datadir)/man +infodir = $(datadir)/info +localstatedir = $(prefix)/var +sharedstatedir = $(prefix)/com + +DESTDIR = + + +# layout stuff +SKIPDIRS = tmp% +INCLUDEDIRS = include% +LIBDIRS = lib% +MODDIRS = mod% +SKIPINSTALL = test% + + +include $(srcdir)/Make.defines + + +ifndef NAME +NAME = $(notdir $(srcdir)) +endif + + +ifndef subdirs +ifeq ($(TARGET),.MAIN) +# for better performance... +subdirs := $(patsubst %/,%,$(wildcard */)) +else +subdirs := $(patsubst $(srcdir)/%/,%,$(filter %/,$(wildcard $(srcdir)/*/))) +endif +subdirs := $(filter-out $(SKIPDIRS), $(subdirs)) +endif + +install install-%: subdirs := $(filter-out $(SKIPINSTALL), $(subdirs)) + + +override MAKE += srcdir=$(srcdir) subdirs="$(subdirs)" shared=$(shared) + + +INCLUDEDIRS := $(filter $(INCLUDEDIRS), $(subdirs)) +LIBDIRS := $(filter $(LIBDIRS), $(subdirs)) +MODDIRS := $(filter $(MODDIRS), $(subdirs)) +EXEDIRS := $(filter-out $(INCLUDEDIRS) $(LIBDIRS) $(MODDIRS), $(subdirs)) +MODUSERS := $(filter $(MODUSERS), $(subdirs)) +SBINUSERS := $(filter $(SBINUSERS), $(subdirs)) + +LIBDIRS := $(filter-out $(LIBLAST),$(LIBDIRS)) $(filter $(LIBDIRS),$(LIBLAST)) + + +includes = $(foreach dir, $(INCLUDEDIRS) $(LIBDIRS), $(srcdir)/$(dir)) + +ifneq ($(gcc),) +CPATH = $(subst $(empty) ,:,$(includes)) +export CPATH +else +override CPPFLAGS += $(patsubst %,-I%,$(includes)) +endif + +libraries = $(foreach dir, $(filter lib%,$(LIBDIRS)), $(srcdir)/$(dir)) + +vpath lib%.so $(libraries) +vpath lib%.a $(libraries) + +_libs = $(strip $(foreach _lib,$(LIBDIRS),\ + $(if $(filter lib%,$(_lib)),\ + $(patsubst lib%,-l%,$(_lib)),\ + $(wildcard $(srcdir)/$(_lib)/$(_lib).so \ + $(srcdir)/$(_lib)/$(_lib).a)))) + +override LIBS := $(_libs) -lm $(LIBS) + +ifeq ($(CC),gcc) +LIBRARY_PATH = $(subst $(empty) ,:,$(libraries)) +export LIBRARY_PATH +else +override LIBS += $(patsubst %,-L%,$(libraries)) +endif + +LIBDEPS = $(filter-out -L%,$(LIBS)) + + +# +# SUBDIRS STUFF +# + +ifneq ($(TARGET),.MAIN) + +obj := o +ifeq ($(shared),yes) +ifneq ($(PIC),no) +ifeq ($(filter $(TARGET),$(LIBDIRS) $(MODDIRS) .MODULE),$(TARGET)) +obj := lo +endif +endif +endif + +sources = $(wildcard *.c) +OBJS = $(sources:.c=.$(obj)) + + +.PHONY: dummy all depend install clean force + +dummy: all + + +clean: + rm -f *.o *.a *.lo *.so $(TARGET) core a.out + + +ifneq ($(sources),) +depend: $(sources) + $(DEPEND) $(CFLAGS) $(CPPFLAGS) $^ >.depend +else +depend: + @true +endif + + +%.o: %.c + $(CC) $(CFLAGS) $(CPPFLAGS) -c $*.c + +%.lo: %.c + $(CC) -fPIC $(CFLAGS) $(CPPFLAGS) -o $*.lo -c $*.c + +%.o: %.s + $(AS) -o $*.o $*.s + +%.o: %.S + $(CC) -traditional $(CPPFLAGS) -c $*.S + +# include if it is present only... +ifeq (.depend, $(wildcard .depend)) +include .depend +endif + +endif + +# +# ...end of SUBDIRS STUFF +# + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0a5b5b8 --- /dev/null +++ b/Makefile @@ -0,0 +1,117 @@ +# +# Copyright (c) 2000, 2001 Dmitry Butskoy +# +# License: GPL v2 or any later +# +# See COPYING for the status of this software. +# + +# +# Global Makefile. +# Global rules, targets etc. +# +# See Make.defines for specific configs. +# + + +srcdir = $(CURDIR) + +override TARGET := .MAIN + +dummy: all + +include ./Make.rules + + +targets = $(EXEDIRS) $(LIBDIRS) $(MODDIRS) + + +# be happy, easy, perfomancy... +.PHONY: $(subdirs) dummy all force +.PHONY: depend indent clean distclean libclean release store libs mods + + +allprereq := $(EXEDIRS) + +ifneq ($(LIBDIRS),) +libs: $(LIBDIRS) +ifneq ($(EXEDIRS),) +$(EXEDIRS): libs +else +allprereq += libs +endif +endif + +ifneq ($(MODDIRS),) +mods: $(MODDIRS) +ifneq ($(MODUSERS),) +$(MODUSERS): mods +else +allprereq += mods +endif +ifneq ($(LIBDIRS),) +$(MODDIRS): libs +endif +endif + +all: $(allprereq) + +depend install: $(allprereq) + +$(foreach goal,$(filter install-%,$(MAKECMDGOALS)),\ + $(eval $(goal): $(patsubst install-%,%,$(goal)))) + + +what = all +depend: what = depend +install install-%: what = install + +ifneq ($(share),) +$(share): shared = yes +endif +ifneq ($(noshare),) +$(noshare): shared = +endif + + +$(targets): mkfile = $(if $(wildcard $@/Makefile),,-f $(srcdir)/default.rules) + +$(targets): force + @$(MAKE) $(mkfile) -C $@ $(what) TARGET=$@ + +force: + + +indent: + find . -type f -name "*.[ch]" -print -exec $(INDENT) {} \; + +clean: + rm -f $(foreach exe, $(EXEDIRS), ./$(exe)/$(exe)) nohup.out + rm -f `find . \( -name "*.[oa]" -o -name "*.[ls]o" \ + -o -name core -o -name "core.[0-9]*" -o -name a.out \) -print` + +distclean: clean + rm -f `find $(foreach dir, $(subdirs), $(dir)/.) \ + \( -name "*.[oa]" -o -name "*.[ls]o" \ + -o -name core -o -name "core.[0-9]*" -o -name a.out \ + -o -name .depend -o -name "_*" -o -name ".cross:*" \) \ + -print` + + +libclean: + rm -f $(foreach lib, $(LIBDIRS), ./$(lib)/$(lib).a ./$(lib)/$(lib).so) + + +# Rules to make whole-distributive operations. +# + +STORE_DIR = $(HOME)/pub + +release release1 release2 release3: + @./chvers.sh $@ + @$(MAKE) store + +store: distclean + @./store.sh $(NAME) $(STORE_DIR) + + diff --git a/README b/README new file mode 100644 index 0000000..8f25a7f --- /dev/null +++ b/README @@ -0,0 +1,39 @@ +This is a new modern implementation of the traceroute(8) +utility for Linux systems. + +Traceroute tracks the route packets taken from an IP network on their +way to a given host. It utilizes the IP protocol's time to live (TTL) +field and attempts to elicit an ICMP TIME_EXCEEDED response from each +gateway along the path to the host. + +Main features: +- Full support for both IPv4 and IPv6 protocols +- Several tracerouting methods, including: + * UDP datagrams (including udplite and udp to particlular port) + * ICMP ECHO packets (including dgram icmp sockets) + * TCP SYNs (in general, any TCP request with various flags and options) + * DCCP Request packets + * Generic IP datagrams +- UDP methods do not require root privileges +- Ability to send several probe packets at a time +- Ability to compute a proper time to wait for each probe +- perform AS path lookups for returned addresses +- show ICMP extensions, including MPLS +- perform path MTU discovery automatically +- show guessed number of hops in backward direction +- command line compatible with the original traceroute +- and much more, see traceroute(8) + +This code was written from the scratch, using some ideas of +Olaf Kirch's traceroute, the original implementation of Van Jacobson +(which was long used before) and some current BSD's ones. + +This traceroute requires Linux kernel 2.6 and higher. + +You can try to contact the author at . + + +Good tracerouting! + +Dmitry Butskoy + diff --git a/TODO b/TODO new file mode 100644 index 0000000..bdd878e --- /dev/null +++ b/TODO @@ -0,0 +1,33 @@ +* It seems that `-l' and `-g' will not work correctly together (IPv6). + + Wait for "flow label" API to be appeared in kernel-headers and + think about it immediately after that... :) + + +* The "final hop" issue. + + All methods, usable when firewalls are present in the network path, + normally use some particular destination port. Most often it is a port + of an already running application. + + It requires that the packet sent should be correct for such an application + (for example, for tracing with udp to port 53, it should be correct DNS + request), and the application normally should answer something on it. + (TCP has no such an issue, as there are just syn to, and ack or reset from). + + In general, we should fill the packet's data depending on the dest port + and protocol. It seems not a task for traceroute itself, it could be + some cmdline option or even external hook... + + +* Think about SCTP method. + + +* Think about "multicast tracerouting" (mrouted(8) and other). + The idea is to increase the room in the mtrace packet step-by-step + (as well as we increase ttl). It seems that if there is no more space + in the probe's room, the mrouted(8) daemon answers immediately, the same + way as if it is a final hop. + + For IPv6 mtrace, there is an RFC draft for this already... + diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..2032885 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +#define VERSION 2.1.2 diff --git a/chvers.sh b/chvers.sh new file mode 100755 index 0000000..2d4b8f2 --- /dev/null +++ b/chvers.sh @@ -0,0 +1,102 @@ +#!/bin/sh +# +# Copyright (c) 2000, 2001 Dmitry Butskoy +# +# License: GPL v2 or any later +# +# See COPYING for the status of this software. +# + +# +# Change version script. +# Normally invoked by a Makefile. +# +# Changes version info in directory name, in VERSION file +# and rpm spec file (if some of these are used). +# +# `release3' changes: 0.2.7 --> 0.2.8 +# `release2' changes: 0.2.7 --> 0.3.0 +# `release1' changes: 0.2.7 --> 1.0.0 +# etc. +# `release' without a digit increment the last number used. +# +# + + +[ $# -lt 1 ] && { + echo "Usage: $0 release[123...0]" >&2 + exit 2 +} + +level=`expr $1 : '.*\([0-9]\)$'` +[ -z "$level" ] && level=0 + +main_dir=`basename \`pwd\`` + + +# Find current version info. + +dir_v="" +file_v="" + +dir_v=`expr $main_dir : '.*-\([0-9.]*\)$'` +[ -r VERSION ] && file_v=`awk '/^ *#/ { print $3 ; exit; } + /^ *VERSION *=/ {split ($0, a, "="); print a[2]; exit; } + { print $0; exit; }' < VERSION ` +[ -n "$file_v" ] && file_v=`echo $file_v ` # to strip possible spaces + +[ -z "$file_v" -a -z "$dir_v" ] && { + echo "$0: Cannot determine version (use dirname postfix or VERSION file)" >&2 + exit 2 +} + +[ -n "$file_v" -a -n "$dir_v" -a "$dir_v" != "$file_v" ] && { + echo "$0: Different version from dirname postfix and VERSION file" >&2 + exit 2 +} + +version="$dir_v" +[ -z "$version" ] && version="$file_v" + + +# Increment current version, as specified by level. + +new_version=`echo $version | awk '{ + level = '"$level"'; + n = split ($0, a, "."); + if (level == 0) level = n; + for (i = 1; i <= n; i++) { + if (i == level) a[i] = a[i] + 1 ; + else if (i > level) a[i] = 0 ; + } + str = a[1] + for (i = 2; i <= n; i++) str = str "." a[i] + print str +}' 2>/dev/null ` + + +# Adjust VERSION file, if any. + +# it is ugly, because $version contains dots... +[ -n "$file_v" ] && { + sed "s/$version/$new_version/" < VERSION > VERSION.new && mv -f VERSION.new VERSION +} + +# Adjust rpm .spec file, if any. +for spec in *.spec +do + [ -f $spec ] || continue + + grep '^Version:[ ]*'"$version" $spec >/dev/null 2>&1 || continue + sed '/^Version:[ ]*'"$version/ s/$version/$new_version/" < $spec > ${spec}.new && mv -f ${spec}.new $spec +done + + +# Adjust dirname postfix, if any. + +[ -n "$dir_v" ] && { + base=`expr \`pwd\` : '^\(.*\)-'"$version"'$' ` + mv -f ${base}-$version ${base}-$new_version +} + +exit 0 diff --git a/default.rules b/default.rules new file mode 100644 index 0000000..cc3bdf8 --- /dev/null +++ b/default.rules @@ -0,0 +1,193 @@ +# +# Copyright (c) 2000, 2001, 2007 Dmitry Butskoy +# +# License: GPL v2 or any later +# +# See COPYING for the status of this software. +# + +# +# Default a target`s Makefile. Useful for any things... +# For some changes, copy it to a dir where needed and edit that copy. +# + + +ifndef srcdir +rul := Make.rules +path := $(word 1, $(wildcard ../$(rul) ../../$(rul))) +ifeq ($(path),../$(rul)) +srcdir = $(dir $(CURDIR)) +endif +ifeq ($(path),../../$(rul)) +srcdir = $(dir $(patsubst %/,%,$(dir $(CURDIR)))) +TARGET = .MODULE +endif +ifeq ($(srcdir),) +$(error Cannot find srcdir (where $(rul) exists)) +endif +endif + +ifndef TARGET +TARGET = $(notdir $(CURDIR)) +endif + +include $(srcdir)/Make.rules + + +# +# LIBDIRS DEFAULTS +# +ifeq ($(filter $(TARGET),$(LIBDIRS)),$(TARGET)) + +ifeq ($(shared),yes) + +all: $(TARGET).so + +$(TARGET).so: $(OBJS) + $(CC) -shared -o $@ -Wl,-soname -Wl,$@ $(OBJS) + +else + +all: $(TARGET).a + +$(TARGET).a: $(OBJS) + rm -f $@ + $(AR) -rc $@ $(OBJS) + $(RANLIB) $@ + +endif + +install_what = $(wildcard $(TARGET).so $(TARGET).a) +install_dir = $(libdir) +install_includes = $(wildcard *.h) + +cross_stamp_file = $(wildcard .cross:*) +ifeq ($(cross_stamp_file),) +cross_stamp = .cross: +else +cross_stamp = $(cross_stamp_file) +endif +new_stamp = .cross:$(subst $(empty) ,:,$(CROSS)) +ifneq ($(cross_stamp),$(new_stamp)) + +$(OBJS): force + +force: + rm -f $(cross_stamp) > $(new_stamp) + @rm -f *.o *.a *.lo *.so + +endif + +endif + + +# +# MODDIRS DEFAULTS +# +ifeq ($(filter $(TARGET),$(MODDIRS)),$(TARGET)) + +modules = $(filter-out $(SKIPDIRS), $(patsubst %/,%,$(wildcard */))) + +.PHONY: $(modules) + +what = all +depend: what = depend +clean: what = clean + +all depend clean: $(modules) + +$(modules): mkfile = $(if $(wildcard $@/Makefile),,-f $(srcdir)/default.rules) + +$(modules): force + @$(MAKE) $(mkfile) -C $@ $(what) TARGET=.MODULE MODULE=$@ + +force: + +install_what = $(wildcard *.so) +install_dir = $(libexecdir) + +endif + + +# +# MODDIRS` subdirectories (i.e., modules) defaults +# +ifeq ($(TARGET),.MODULE) + +ifndef MODULE +MODULE = $(notdir $(CURDIR)) +endif + +ifeq ($(shared),yes) + +all: ../$(MODULE).so + +../$(MODULE).so: $(OBJS) $(LIBDEPS) + $(CC) -shared -o $@ $(OBJS) $(LIBS) + +else + +all: ../$(MODULE).o + +../$(MODULE).o: $(OBJS) + $(LD) -r -o $@ $(OBJS) + +endif + +endif + + +# +# EXEDIRS DEFAULTS (for usual executables) +# +ifeq ($(filter $(TARGET),$(EXEDIRS)),$(TARGET)) + +ifeq ($(filter $(TARGET),$(MODUSERS)),$(TARGET)) +MOD_OBJS = $(wildcard $(foreach dir,$(MODDIRS),$(srcdir)/$(dir)/*.o)) +ifeq ($(shared),yes) +override LDFLAGS := -rdynamic $(LDFLAGS) +endif +install_includes= $(wildcard $(foreach dir,$(INCLUDEDIRS),$(srcdir)/$(dir)/*.h)) +install_includes:= $(filter-out $(srcdir)/include/version.h,$(install_includes)) +else +MOD_OBJS = +endif + +all: $(TARGET) + +$(TARGET): $(OBJS) $(MOD_OBJS) $(LIBDEPS) + $(CC) $(LDFLAGS) -o $@ $(OBJS) $(MOD_OBJS) $(LIBS) + +install_what = $(wildcard $(TARGET)) +install_dir = $(if $(filter $(TARGET),$(SBINUSERS)),$(sbindir),$(bindir)) + +endif + + +# +# Install stuff +# + +install_manuals = $(wildcard *.[0-9] *.[0-9]?) + + +install: +ifneq ($(install_dir),) + @mkdir -p $(DESTDIR)$(install_dir) +endif +ifneq ($(install_what),) + $(INSTALL) $(install_what) $(DESTDIR)$(install_dir) +endif +ifneq ($(install_includes),) + @mkdir -p $(DESTDIR)$(includedir) + $(INSTALL) $(install_includes) $(DESTDIR)$(includedir) +endif + @true +ifneq ($(install_manuals),) + $(foreach man,$(install_manuals),$(call inst_man,$(man))) +define inst_man +@mkdir -p $(DESTDIR)$(mandir)/man$(patsubst .%,%,$(suffix $(1))) + cp -f $(1) $(DESTDIR)$(mandir)/man$(patsubst .%,%,$(suffix $(1))) + +endef +endif diff --git a/include/version.h b/include/version.h new file mode 100644 index 0000000..ddf3b06 --- /dev/null +++ b/include/version.h @@ -0,0 +1,9 @@ +/* + Copyright (c) 2006, 2007 Dmitry Butskoy + + License: GPL v2 or any later + + See COPYING for the status of this software. +*/ + +#include "../VERSION" diff --git a/libsupp/clif.c b/libsupp/clif.c new file mode 100644 index 0000000..4ef20e4 --- /dev/null +++ b/libsupp/clif.c @@ -0,0 +1,1259 @@ +/* + Copyright (c) 2000, 2003 Dmitry Butskoy + + License: LGPL v2.1 or any later + + See COPYING.LIB for the status of this software. +*/ + +#include +#include +#include +#include + +#include "clif.h" + + +#if 1 /* Bad idea, anyway... */ +#define MAX_ARGC_NUMBER 256 +typedef unsigned char _CLIF_index; +#else +#define MAX_ARGC_NUMBER (4096 / 5 + 1) /* POSIX ARG_MAX >= 4096 ... */ +typedef unsigned short _CLIF_index; +#endif + + +/* This is needed for some print info functions. + This is ugly for thread-safe (is it really actual on program invoking?), + and for several CLIF_parse_cmdline invoking... But foo on this. Yeah... +*/ +static struct { + int argc; + char **argv; + CLIF_option *option_list; + CLIF_argument *argument_list; + unsigned int parse_flags; +} curr = { 0, }; + + +static void err_report (const char *format, ...) { + va_list ap; + + if (curr.parse_flags & CLIF_SILENT) + return; + + va_start (ap, format); + + vfprintf (stderr, format, ap); + + va_end (ap); + + fprintf (stderr, "\n"); + + return; +} + + +/* info generation stuff... */ + +#define SHORT_PLUS_MINUS "+/-" +#define LONG_PLUS_MINUS "++/--" +#define EXCL_DLM " | " + +static char *show_short (const CLIF_option *optn) { + static char buf[80]; + char *p = buf; + unsigned int flags = optn->flags | curr.parse_flags; + + if (optn->function_plus) { + if (!optn->function) *p++ = '+'; + else { + strcpy (p, SHORT_PLUS_MINUS); + p += sizeof (SHORT_PLUS_MINUS) - 1; + } + } else + *p++ = '-'; + + *p++ = optn->short_opt[0]; + + if (optn->arg_name) { + char *endp = buf + sizeof (buf) - sizeof (",...]"); + const char *s; + + if (!(flags & _CLIF_STRICT_JOIN_ARG)) *p++ = ' '; + if (flags & CLIF_OPTARG) *p++ = '['; + + s = optn->arg_name; + while (*s && p < endp) *p++ = *s++; + + if (flags & CLIF_SEVERAL) { + strcpy (p, ",..."); + p += sizeof (",...") - 1; /* last '\0' ... */ + } + + if (flags & CLIF_OPTARG) *p++ = ']'; + } + + *p = '\0'; + + return buf; +} + +static char *show_long (const CLIF_option *optn) { + static char buf[80]; + char *p = buf; + char *endp; + const char *s; + unsigned int flags = optn->flags | curr.parse_flags; + + + if (!(flags & _CLIF_STRICT_KEYWORD)) { + + if (!(flags & _CLIF_STRICT_ONEDASH)) { + if (optn->function_plus) { + if (!optn->function) { *p++ = '+'; *p++ = '+'; } + else { + strcpy (p, LONG_PLUS_MINUS); + p += sizeof (LONG_PLUS_MINUS) - 1; + } + } else { *p++ = '-'; *p++ = '-'; } + + } else { + if (optn->function_plus) { + if (!optn->function) *p++ = '+'; + else { + strcpy (p, SHORT_PLUS_MINUS); + p += sizeof (SHORT_PLUS_MINUS) - 1; + } + } else *p++ = '-'; + } + } + + s = optn->long_opt; + endp = buf + sizeof (buf) - sizeof (" ["); + while (*s && p < endp) *p++ = *s++; + + if (optn->arg_name) { + + if (flags & _CLIF_STRICT_NOEQUAL) { + *p++ = ' '; + if (flags & CLIF_OPTARG) *p++ = '['; + } else { + if (flags & CLIF_OPTARG) *p++ = '['; + *p++ = '='; + } + + s = optn->arg_name; + endp = buf + sizeof (buf) - sizeof (",...]"); + while (*s && p < endp) *p++ = *s++; + + if (flags & CLIF_SEVERAL) { + strcpy (p, ",..."); + p += sizeof (",...") - 1; /* last '\0' ... */ + } + + if (flags & CLIF_OPTARG) *p++ = ']'; + } + + *p = '\0'; + + return buf; +} + +static char *show_excl (const CLIF_option *option_list, int *cnt_p) { + static char buf[256]; + const CLIF_option *optn; + char *p = buf; + char *endp = buf + sizeof (buf) - sizeof (EXCL_DLM); + int excl_cnt = 0; + + *p = '\0'; + if (cnt_p) *cnt_p = 0; + if (!option_list) return buf; + + for (optn = option_list; optn->short_opt || optn->long_opt; optn++) { + char *s; + + if (!(optn->flags & CLIF_EXCL)) continue; + + if (optn->short_opt) s = show_short (optn); + else s = show_long (optn); + + if (excl_cnt > 0) { /* i.e., second etc... */ + strcpy (p, EXCL_DLM); + p += sizeof (EXCL_DLM) - 1; + } + + while (*s && p < endp) *p++ = *s++; + + excl_cnt++; + } + + *p = '\0'; + + if (cnt_p) *cnt_p = excl_cnt; + + return buf; +} + + +static int is_keyword (const CLIF_option *optn) { + unsigned int flags = optn->flags | curr.parse_flags; + + return (flags & _CLIF_STRICT_KEYWORD) != 0; +} + + +static void err_bad_opt (const char *arg, char c, int n) { + char sym = (*arg == '+') ? '+' : '-'; + + if (c) err_report ("Bad option `%c%c' (argc %d)", sym, c, n); + else { + char *p = strchr (arg, '='); + const char *type = (*arg == sym) ? "option" : "keyword"; + + if (p) + err_report ("Bad %s `%s' (with arg `%s') (argc %d)", + type, arg, p + 1, n); + else + err_report ("Bad %s `%s' (argc %d)", type, arg, n); + } +} + +static void err_bad_arg (const CLIF_option *optn, char c, int n) { + CLIF_option tmp = *optn; + char ss[80]; + char *s; + + tmp.arg_name = NULL; + + if (c) { + s = show_short (&tmp); /* always without arg... */ + strncpy (ss, s, sizeof (ss)); + s = show_short (optn); + } else { + s = show_long (&tmp); /* always without arg... */ + strncpy (ss, s, sizeof (ss)); + s = show_long (optn); + } + + err_report ("%s `%s' (argc %d) requires an argument: `%s'", + (c || !is_keyword (optn)) ? "Option" : "Keyword", ss, n, s); +} + +static void err_bad_res (const CLIF_option *optn, char c, + const char *opt_arg, int n) { + CLIF_option tmp = *optn; + char *ss; + const char *type; + + tmp.arg_name = NULL; + + if (c) { + ss = show_short (&tmp); + type = "option"; + } else { + ss = show_long (&tmp); + type = is_keyword (optn) ? "keyword" : "option"; + } + + if (optn->arg_name) + err_report ("Cannot handle `%s' %s with arg `%s' (argc %d)", + ss, type, opt_arg, n); + else + err_report ("Cannot handle `%s' %s (argc %d)", ss, type, n); +} + +static void err_bad_excl (const CLIF_option *optn, char c, int n) { + CLIF_option tmp = *optn; + char *ss; + char *excl = show_excl (curr.option_list, 0); + /* Note: show_(short|long)() nested!!! */ + + tmp.arg_name = NULL; + + if (c) ss = show_short (&tmp); + else ss = show_long (&tmp); + + err_report ("%s `%s' (argc %d): Only one of:\n %s\n" + "may be specified.", + (c || !is_keyword (optn)) ? "Option" : "Keyword", + ss, n, excl); +} + + +static CLIF_option *find_long (char *arg, char **arg_p, + unsigned int match, unsigned int nomatch) { + CLIF_option *optn; + CLIF_option *abbrev = NULL; + char *abbrev_arg = NULL; + int abbrev_found = 0; + + + for (optn = curr.option_list; + optn->short_opt || optn->long_opt; + optn++ + ) { + char *a; + const char *o; + unsigned int flags; + + if (!optn->long_opt) continue; + + flags = curr.parse_flags | optn->flags; + if (flags & nomatch) continue; + if (match && !(flags & match)) continue; /* XXX: optimize it */ + + + for (a = arg, o = optn->long_opt; *o && *a == *o; a++, o++) ; + + if (*a == '\0' || + (*a == '=' && optn->arg_name && !(flags & _CLIF_STRICT_NOEQUAL)) + ) { /* looks like end of option... */ + + if (!*o) { /* explicit match found */ + if (*a == '=' && arg_p) *arg_p = a + 1; + return optn; + } + + if ((flags & CLIF_ABBREV) && + (a - arg >= CLIF_MIN_ABBREV) + ) { + if (!abbrev_found) { + abbrev_found = 1; + abbrev = optn; + if (*a == '=') abbrev_arg = a + 1; + } else /* several possibility case... */ + abbrev = NULL; + } + } + } + + if (abbrev) { /* implicit match found */ + if (abbrev_arg && arg_p) *arg_p = abbrev_arg; + return abbrev; + } else /* no match found */ + return NULL; +} + +static int check_sym (const CLIF_option *optn, char sym) { + + if (sym == '+') { + if (!optn->function_plus) return -1; + } + else if (sym == '-') { + if (!optn->function && optn->function_plus) + return -1; + } + + return 0; +} + +static int call_function (CLIF_option *optn, char *opt_arg, char sym) { + int (*function) (CLIF_option *, char *); + + function = (sym == '+') ? optn->function_plus : optn->function; + + if (!function) return 0; + + if (opt_arg && ((optn->flags | curr.parse_flags) & CLIF_SEVERAL)) { + char tmp[80]; + char *t; + char *endt = tmp + sizeof (tmp); + + while (*opt_arg) { + + t = tmp; + while (t < endt && *opt_arg && + *opt_arg != ' ' && *opt_arg != '\t' && *opt_arg != ',' + ) *t++ = *opt_arg++; + + if (t >= endt) return -1; + + *t = '\0'; + + if (function (optn, tmp) < 0) return -1; + + while (*opt_arg == ' ' || *opt_arg == '\t' || *opt_arg == ',') + opt_arg++; + } + + return 0; + } + + return function (optn, opt_arg); +} + + +int CLIF_parse_cmdline (int argc, char *argv[], + CLIF_option *option_list, + CLIF_argument *argument_list, + unsigned int parse_flags) { + int i, j; + CLIF_option *optn; + CLIF_argument *argm; + int num_args = 0; + int num_argm = 0, strict_beg = 0, strict_end = 0; + _CLIF_index arg_n[MAX_ARGC_NUMBER]; + unsigned int dirty_flags = 0; + int dirty_plus = 0; + int exclusive_cnt = 0; + int posix = getenv ("POSIXLY_CORRECT") != NULL || + (parse_flags & CLIF_POSIX); + + curr.argc = argc; + curr.argv = argv; + curr.option_list = option_list; + curr.argument_list = argument_list; + curr.parse_flags = parse_flags; + + if (argc <= 1 && (parse_flags & CLIF_HELP_EMPTY)) { + CLIF_current_help (); + exit (0); + } + + /* Scan argument_list for check and some info. */ + + if (argument_list) { + enum stages { STRICT_BEG, OPTIONAL, STRICT_END }; + int stage = STRICT_BEG; + + for (argm = argument_list; argm->name; argm++) { + + if (argm->flags & CLIF_STRICT) { + + if (stage == STRICT_BEG) strict_beg++; + else if (stage == OPTIONAL) { + stage = STRICT_END; + strict_end++; + } + else if (stage == STRICT_END) + strict_end++; + } else { + if (stage == STRICT_BEG) stage = OPTIONAL; + else if (stage == STRICT_END) { + err_report ("Incorrect argument list set in program " + "source: more than one optional area."); + return -1; + } + } + + num_argm++; + } + } + + /* Scan option_list for some info. */ + if (option_list) { + + dirty_flags = parse_flags; + + for (optn = option_list; + optn->short_opt || optn->long_opt; + optn++ + ) { + dirty_flags |= optn->flags; + if (optn->function_plus) dirty_plus = 1; + } + } + + if (dirty_flags & CLIF_EXCL) + exclusive_cnt = 1; /* only one is allowed... */ + + + /* Go ! Store arguments, parse options. */ + + for (i = 1; i < argc; i++) { + char *arg = argv[i]; + char *opt_arg = NULL; + char sym = '-'; + + if (!option_list) + goto handle_arg; + + if (*arg == '+' && dirty_plus) + sym = '+'; + + if (*arg != sym) { /* argument or keyword */ + + if (dirty_flags & CLIF_MAY_KEYWORD) { + optn = find_long (arg, &opt_arg, CLIF_MAY_KEYWORD, 0); + if (optn) goto long_found; + } + + if (num_args == 0 && (parse_flags & CLIF_FIRST_GROUP)) { + /* ugly... */ + parse_flags &= ~CLIF_FIRST_GROUP; + dirty_flags &= ~CLIF_FIRST_GROUP; /* to be correct */ + + goto handle_short; + } + + /* else it is an argument */ + goto handle_arg; + + } + else if (*++arg == sym) { /* `--' - long option */ + arg++; + + if (*arg == sym || /* `---' - let it be not option... */ + (parse_flags & (_CLIF_STRICT_KEYWORD|_CLIF_STRICT_ONEDASH)) + ) { + arg -= 2; + goto handle_arg; /* not option anyway */ + } + + optn = find_long (arg, &opt_arg, 0, + _CLIF_STRICT_KEYWORD | _CLIF_STRICT_ONEDASH); + if (optn) goto long_found; + + /* XXX: May be allow only for `--', not `++' too... */ + if (!*arg && sym == '-') { /* `--' and no empty longoption */ + option_list = NULL; /* POSIX way... */ + continue; + } + + /* XXX: or treat as an argument sometimes??? */ + err_bad_opt (argv[i], 0, i); + return -1; + } + else { /* short option, or several short options... */ + + if (dirty_flags & CLIF_MAY_ONEDASH) { + optn = find_long (arg, &opt_arg, CLIF_MAY_ONEDASH, 0); + if (optn) goto long_found; + } + + if (!*arg) { /* POSIX say: only "stdout specification"... */ + arg--; + goto handle_arg; + } + + goto handle_short; + } + + + long_found: + if (check_sym (optn, sym) < 0) { /* Oops... */ + err_bad_opt (argv[i], 0, i); + return -1; + } + + if (optn->flags & CLIF_EXCL) { + if (!exclusive_cnt) { + err_bad_excl (optn, 0, i); + return -1; + } + exclusive_cnt--; + } + + if (optn->arg_name && !opt_arg) { + unsigned int flags = optn->flags | parse_flags; + + if (++i >= argc || + !(flags & CLIF_MAY_NOEQUAL) + ) { /* missing opt arg */ + i--; + + if (!(flags & CLIF_OPTARG)) { + err_bad_arg (optn, 0, i); + return -1; + } + + opt_arg = NULL; + } else + opt_arg = argv[i]; + + } + + + if (call_function (optn, opt_arg, sym) < 0) { + err_bad_res (optn, 0, opt_arg, i); + return -1; + } + + if (optn->flags & CLIF_EXIT) + exit (0); + + continue; + + + handle_arg: + if (argument_list) { + if (i < MAX_ARGC_NUMBER) /* XXX: ugly, better report */ + arg_n[num_args++] = i; + } else { + err_report ("`%s' (argc %d): arguments are not allowed", + argv[i], i); + return -1; + } + + /* POSIX say: No more options after args... */ + if (posix) option_list = NULL; /* geniously... */ + + continue; + + + handle_short: + + opt_arg = NULL; + + do { + + for (optn = option_list; + optn->short_opt || optn->long_opt; + optn++ + ) { + if (optn->short_opt && optn->short_opt[0] == *arg) + break; + } + if (!optn->short_opt || + check_sym (optn, sym) < 0 + ) { + err_bad_opt (argv[i], *arg, i); + return -1; + } + + if (optn->flags & CLIF_EXCL) { + if (!exclusive_cnt) { + err_bad_excl (optn, *arg, i); + return -1; + } + exclusive_cnt--; + } + + + if (optn->arg_name) { + unsigned int flags = parse_flags | optn->flags; + + if (arg[1] == '\0') { /* a last one */ + + /* POSIX say: an option with arg cannot be grouped. */ + if (posix && arg != argv[i] && arg[-1] != sym) { + err_bad_arg (optn, *arg, i); /* good way? */ + return -1; + } + + if (++i >= argc || + (flags & _CLIF_STRICT_JOIN_ARG) + ) { + i--; + + if (!(flags & CLIF_OPTARG)) { + err_bad_arg (optn, *arg, i); + return -1; + } + + opt_arg = NULL; + } else + opt_arg = argv[i]; + } + else if ((arg == argv[i] || arg[-1] == sym) && + (flags & CLIF_MAY_JOIN_ARG) + ) { + opt_arg = ++arg; + } + else { /* inside a group... */ + if (!(flags & CLIF_OPTARG) || + (flags & CLIF_MAY_JOIN_ARG) + ) { + err_bad_arg (optn, *arg, i); + return -1; + } + + opt_arg = NULL; + } + } + + if (call_function (optn, opt_arg, sym) < 0) { + err_bad_res (optn, optn->short_opt[0], opt_arg, i); + return -1; + } + + if (optn->flags & CLIF_EXIT) + exit (0); + + } while (!opt_arg && *++arg); + + } /* for ( ... ) */ + + + if ((parse_flags & CLIF_STRICT_EXCL) && exclusive_cnt != 0) { + err_report ("One of these must be specified:\n %s\n", + show_excl (option_list, 0)); + return -1; + } + + + /* Now, after *ALL* options, handle arguments, if any. */ + + if (num_args < strict_beg + strict_end) { + /* Missing some needed arguments. */ + + if (num_args < strict_beg) argm = argument_list + num_args; + else + argm = argument_list + + ((num_args - strict_beg) + (num_argm - strict_end)); + + if (num_args == strict_beg + strict_end - 1) + err_report ("Specify \"%s\" missing argument.", argm->name); + else + err_report ("Specify \"%s\" and other missing arguments.", + argm->name); + return -1; + } + + if (num_args > 0) { + _CLIF_index argm_index[MAX_ARGC_NUMBER]; + + /* assing argm (by index) for each arg... */ + + for (i = 0, j = 0; i < strict_beg; i++, j++) + argm_index[i] = j; + for (i = num_args - strict_end, j = num_argm - strict_end; + i < num_args; i++, j++ + ) argm_index[i] = j; + for (i = strict_beg, j = strict_beg; + i < num_args - strict_end && j < num_argm - strict_end; + i++ + ) { + argm_index[i] = j; + if (!(argument_list[j].flags & CLIF_MORE)) + j++; + } + + if (i < num_args - strict_end) { /* there are extra args... */ + err_report ("Extra arg `%s' (position %d, argc %d)", + argv[arg_n[i]], i + 1, arg_n[i]); + return -1; + } + + if (j < num_argm - strict_end && + !(argument_list[j].flags & CLIF_MORE) && + /* ...i.e, there are some missing optional args... */ + (argument_list[j].flags & CLIF_ACC_PREV) + ) { + if (j == 0) + err_report ("Incorrect argument list set: first arg " + "cannot be `accompanied with previous'."); + else + err_report ("Arg \"%s\" must be specified because " + "\"%s\" `%s' is used.", argument_list[j].name, + argument_list[j - 1].name, argv[arg_n[i - 1]]); + return -1; + } + + if (argm_index[--i] == j && + /* above is true only after OPTIONAL area scan + and when `j' is stopped on CLIF_MORE */ + ++j < num_argm - strict_end + /* i.e: there is a *last* one (after CLIF_MORE) + in the OPTIONAL area */ + ) argm_index[i] = j; /* *last* is better than *more* */ + + + /* ...and work now */ + + for (i = 0; i < num_args; i++) { + argm = argument_list + argm_index[i]; + + if (argm->function && + argm->function (argm, argv[arg_n[i]], i) < 0 + ) { + err_report ("Cannot handle \"%s\" cmdline arg `%s' " + "on position %d (argc %d)", + argm->name, argv[arg_n[i]], i + 1, arg_n[i]); + return -1; + } + } + + /* That`s all. */ + } + + return 0; +} + + +static void box_output (int start, int left, int width, const char *str, + const char *arg_name) { + char *p, *endp, *s; + int l; + char buf[1024]; + char spacer[128]; /* assume it is enough */ + + if (left > sizeof (spacer) - 2) left = sizeof (spacer) - 2; + if (width > sizeof (buf) - 1) width = sizeof (buf) - 1; + + spacer[0] = '\n'; + memset (spacer + 1, ' ', left); + spacer[left + 1] = '\0'; + + + l = left - start; + if (l > 0) { + memset (buf, ' ', l); + buf[l] = '\0'; + fprintf (stderr, "%s", buf); + } else + fprintf (stderr, "%s", spacer); + + + endp = buf + width; + + p = buf; + + while (*str) { + + while (*str && p < endp) { + + if (*str == '%' && arg_name) { + if (str[1] == '%') { + *p++ = '%'; + str += 2; + continue; + } + else if (str[1] == 's') { + const char *a = arg_name; + + while (*a && p < endp) *p++ = *a++; + str += 2; + continue; + } + } + + *p++ = *str++; + } + + *p = '\0'; + + if (p < endp) break; + + + while (p > buf && *p != ' ' && *p != '\t') p--; + if (p <= buf) return; /* foo on you */ + + *p = '\0'; + fprintf (stderr, "%s", buf); + fprintf (stderr, "%s", spacer); + + p++; + for (s = buf; *p; *s++ = *p++) ; + *s = '\0'; + p = s; + } + + + fprintf (stderr, "%s", buf); + + return; +} + + +#define SHORT_LONG_DLM " " +#define OPT_START_DLM " " +#define OPT_FIELD_WIDTH 30 + +#define ARG_MARK_STRICT "+ " +#define ARG_MARK_GROUP0 " . " +#define ARG_MARK_GROUP " ' " +#define ARG_MARK_OPT " " +#define ARG_FIELD_WIDTH 20 + +#define SCREEN_WIDTH 80 + + +void CLIF_print_options (const char *header, + const CLIF_option *option_list) { + const CLIF_option *optn; + char *excl; + int excl_cnt = 0; + + /* Print a header string, if present... */ + if (header) fprintf (stderr, "%s\n", header); + + if (!option_list) return; + + + for (optn = option_list; optn->short_opt || optn->long_opt; optn++) { + int len; + + /* generate and print an option usage */ + + if (optn->short_opt) { + if (optn->long_opt) + len = fprintf (stderr, OPT_START_DLM "%s" + SHORT_LONG_DLM "%s", + show_short (optn), show_long (optn)); + else + len = fprintf (stderr, OPT_START_DLM "%s", + show_short (optn)); + } else + len = fprintf (stderr, OPT_START_DLM "%s", show_long (optn)); + + + /* print a help string, if present */ + + if (optn->help_string) + box_output (len, OPT_FIELD_WIDTH, + SCREEN_WIDTH - OPT_FIELD_WIDTH, + optn->help_string, optn->arg_name); + + fprintf (stderr, "\n"); /* a last one */ + } + + excl = show_excl (option_list, &excl_cnt); + if (excl_cnt > 0) { + + if (excl_cnt == 1) { + if ((curr.parse_flags & CLIF_STRICT_EXCL) && + curr.option_list == option_list + ) fprintf (stderr, "Anyway `%s' must be specified.\n", excl); + else /* simple ordinary option, because excl_cnt == 1 ... */; + } else + fprintf (stderr, "Only one of these may be specified:\n" + " %s\n", excl); + } + + return; +} + + +void CLIF_print_arguments (const char *header, + const CLIF_argument *argument_list) { + const CLIF_argument *argm; + + + if (!argument_list) return; + + /* Print a header string, if present... */ + if (header) fprintf (stderr, "%s\n", header); + + + for (argm = argument_list; argm->name; argm++) { + int len; + + if (argm->flags & CLIF_STRICT) + len = fprintf (stderr, ARG_MARK_STRICT "%s", argm->name); + else if (argm->flags & CLIF_MORE) + len = fprintf (stderr, ARG_MARK_OPT "%s ...", argm->name); + else if (argm->flags & CLIF_ACC_PREV) + len = fprintf (stderr, ARG_MARK_GROUP "%s", argm->name); + else if ((argm + 1)->name && ((argm + 1)->flags & CLIF_ACC_PREV)) + len = fprintf (stderr, ARG_MARK_GROUP0 "%s", argm->name); + else + len = fprintf (stderr, ARG_MARK_OPT "%s", argm->name); + + if (argm->help_string) + box_output (len, ARG_FIELD_WIDTH, + SCREEN_WIDTH - ARG_FIELD_WIDTH, + argm->help_string, argm->name); + + fprintf (stderr, "\n"); + } + + return; +} + + +void CLIF_print_usage (const char *header, const char *progname, + const CLIF_option *option_list, + const CLIF_argument *argument_list) { + + if (!progname && curr.argv) + progname = curr.argv[0]; + + if (!header) { + if (progname) + fprintf (stderr, "Usage: %s", progname); + else + fprintf (stderr, "Command line options:"); + } else { + if (progname) + fprintf (stderr, "%s\n" OPT_START_DLM "%s", header, progname); + else + fprintf (stderr, "%s", header); + } + + + if (option_list) { + const CLIF_option *optn; + char m_buf[256], p_buf[256], mp_buf[256]; + char *m = m_buf, *p = p_buf, *mp = mp_buf; + char *end_m = m_buf + sizeof (m_buf) - 1; + char *end_p = p_buf + sizeof (p_buf) - 1; + char *end_mp = mp_buf + sizeof (mp_buf) - 1; + char *excl; + int excl_cnt = 0; + + + /* first, show exclusive option list, if any... */ + + excl = show_excl (option_list, &excl_cnt); + if (excl_cnt > 0) { + if ((curr.parse_flags & CLIF_STRICT_EXCL) && + curr.option_list == option_list + ) { + if (excl_cnt == 1) + fprintf (stderr, " %s", excl); + else + fprintf (stderr, " { %s }", excl); + } else + fprintf (stderr, " [ %s ]", excl); + } + + + /* second, find short options without arguments... */ + + for (optn = option_list; + optn->short_opt || optn->long_opt; + optn++ + ) { + /* We don`t exclude CLIF_EXTRA hear: + simple one char don`t eat a lot of space... + */ + + if (!optn->short_opt || + optn->arg_name || + (optn->flags & CLIF_EXCL) + ) continue; + + if (optn->function_plus) { + if (optn->function) { + if (mp < end_mp) *mp++ = optn->short_opt[0]; + } else { + if (p < end_p) *p++ = optn->short_opt[0]; + } + } else { + if (m < end_m) *m++ = optn->short_opt[0]; + } + } + + if (m > (char *) m_buf) { + *m = '\0'; + fprintf (stderr, " [ -%s ]", m_buf); + } + if (p > (char *) p_buf) { + *p = '\0'; + fprintf (stderr, " [ +%s ]", p_buf); + } + if (mp > (char *) mp_buf) { + *mp = '\0'; + fprintf (stderr, " [ " SHORT_PLUS_MINUS "%s ]", mp_buf); + } + + + /* third, print all another... */ + + for (optn = option_list; + optn->short_opt || optn->long_opt; + optn++ + ) { + if (optn->flags & CLIF_EXTRA) continue; + + if (optn->flags & CLIF_EXCL) + continue; /* already handled */ + + if (optn->short_opt) { + if (optn->arg_name) + fprintf (stderr, " [ %s ]", show_short (optn)); + else + /* already handled */; + } else + fprintf (stderr, " [ %s ]", show_long (optn)); + } + } + + + if (argument_list) { + const CLIF_argument *argm; + int deep = 0; + + for (argm = argument_list; argm->name; argm++) { + + if (argm->flags & CLIF_STRICT) { + if (deep > 0) { + fputc (' ', stderr); + while (deep--) fputc (']', stderr); + deep = 0; + } + + fprintf (stderr, " %s", argm->name); + } else { + if (argm->flags & CLIF_MORE) + fprintf (stderr, " [ %s ...", argm->name); + else if (argm->flags & CLIF_ACC_PREV) { + fprintf (stderr, " %s", argm->name); + --deep; /* ugly, but easy */ + } else + fprintf (stderr, " [ %s", argm->name); + + deep++; + } + } + + if (deep > 0) { + fputc (' ', stderr); + while (deep--) fputc (']', stderr); + } + } + + + fprintf (stderr, "\n"); +} + + +int CLIF_current_help (void) { + + if (!curr.argc) return -1; /* i.e., not inited... */ + + CLIF_print_usage ("Usage:", curr.argv[0], curr.option_list, + curr.argument_list); + + if (curr.option_list) + CLIF_print_options ("Options:", curr.option_list); + + if (curr.argument_list) + CLIF_print_arguments ("\nArguments:", curr.argument_list); + + return 0; +} + + +/* Common useful option handlers. */ + +int CLIF_version_handler (CLIF_option *optn, char *arg) { + + if (!optn->data) return -1; + + fprintf (stderr, "%s\n", ((char *) optn->data)); + + return 0; /* be happy */ +} + + +int CLIF_set_flag (CLIF_option *optn, char *arg) { + + if (!optn->data) return -1; + + *((int *) optn->data) = 1; + + return 0; +} + + +int CLIF_unset_flag (CLIF_option *optn, char *arg) { + + if (!optn->data) return -1; + + *((int *) optn->data) = 0; + + return 0; +} + + +static int set_string (char **data, char *arg) { + + if (!data) return -1; + + *data = arg; + + return 0; +} + +int CLIF_set_string (CLIF_option *optn, char *arg) { + + return set_string (optn->data, arg); +} + +int CLIF_arg_string (CLIF_argument *argm, char *arg, int index) { + + return set_string (argm->data, arg); +} + + +static int set_int (int *data, char *arg) { + char *q; + + if (!data) return -1; + + *data = (int) strtol (arg, &q, 0); + + return (q == arg || *q) ? -1 : 0; +} + +static int set_uint (unsigned int *data, char *arg) { + char *q; + + if (!data) return -1; + + *data = (unsigned int) strtoul (arg, &q, 0); + + return (q == arg || *q) ? -1 : 0; +} + +static int set_double (double *data, char *arg) { + char *q; + + if (!data) return -1; + + *data = strtod (arg, &q); + + return (q == arg || *q) ? -1 : 0; +} + + +int CLIF_set_int (CLIF_option *optn, char *arg) { + + return set_int (optn->data, arg); +} + +int CLIF_set_uint (CLIF_option *optn, char *arg) { + + return set_uint (optn->data, arg); +} + +int CLIF_set_double (CLIF_option *optn, char *arg) { + + return set_double (optn->data, arg); +} + +int CLIF_arg_int (CLIF_argument *argm, char *arg, int index) { + + return set_int (argm->data, arg); +} + +int CLIF_arg_uint (CLIF_argument *argm, char *arg, int index) { + + return set_uint (argm->data, arg); +} + +int CLIF_arg_double (CLIF_argument *argm, char *arg, int index) { + + return set_double (argm->data, arg); +} + + +int CLIF_call_func (CLIF_option *optn, char *arg) { + + if (!optn->data) return -1; + + if (optn->arg_name) { + int (*func) (char *) = optn->data; + + return func (arg); + } else { + int (*func) (void) = optn->data; + + return func (); + } +} + +int CLIF_arg_func (CLIF_argument *argm, char *arg, int index) { + int (*func) (char *, int); + + if (!argm->data) return -1; + + func = (int (*) (char *, int)) argm->data; + + return func (arg, index); +} + diff --git a/libsupp/clif.h b/libsupp/clif.h new file mode 100644 index 0000000..c798a8d --- /dev/null +++ b/libsupp/clif.h @@ -0,0 +1,121 @@ +/* + Copyright (c) 2000, 2003 Dmitry Butskoy + + License: LGPL v2.1 or any later + + See COPYING.LIB for the status of this software. +*/ + +#ifndef _CLIF_H +#define _CLIF_H + + +typedef struct CLIF_option_struct CLIF_option; +struct CLIF_option_struct { + const char *short_opt; + const char *long_opt; + const char *arg_name; + const char *help_string; + int (*function) (CLIF_option *optn, char *arg); + void *data; + int (*function_plus) (CLIF_option *optn, char *arg); + unsigned int flags; +}; +#define CLIF_END_OPTION { 0, 0, 0, 0, 0, 0, 0, 0 } + +typedef struct CLIF_argument_struct CLIF_argument; +struct CLIF_argument_struct { + const char *name; + const char *help_string; + int (*function) (CLIF_argument *argm, char *arg, int index); + void *data; + unsigned int flags; +}; +#define CLIF_END_ARGUMENT { 0, 0, 0, 0, 0 } + +/* Argument flag bits. */ +#define CLIF_MORE (0x01) /* null or several */ +#define CLIF_STRICT (0x02) /* arg must be present */ +#define CLIF_ACC_PREV (0x04) /* arg must be accompanied with previous */ + + +/* Option flag bits. */ + +/* affected only by per-option flags */ +#define CLIF_EXTRA (0x0001) /* don`t show in usage line */ +#define CLIF_EXIT (0x0002) /* exit after handler return */ +#define CLIF_EXCL (0x0004) /* at exclusive area */ + +/* affected by per-option flags and by common `parse_flags' argument + of CLIF_parse_cmdline(). In last case appropriate bits are translated + for all the options. +*/ +#define CLIF_MAY_JOIN_ARG (0x0010) +#define _CLIF_STRICT_JOIN_ARG (0x0020) +#define CLIF_JOIN_ARG (CLIF_MAY_JOIN_ARG|_CLIF_STRICT_JOIN_ARG) +#define CLIF_MAY_NOEQUAL (0x0040) +#define _CLIF_STRICT_NOEQUAL (0x0080) +#define CLIF_NOEQUAL (CLIF_MAY_NOEQUAL|_CLIF_STRICT_NOEQUAL) +#define CLIF_MAY_KEYWORD (0x0100) +#define _CLIF_STRICT_KEYWORD (0x0200) +#define CLIF_KEYWORD (CLIF_MAY_KEYWORD|_CLIF_STRICT_KEYWORD) +#define CLIF_MAY_ONEDASH (0x0400) +#define _CLIF_STRICT_ONEDASH (0x0800) +#define CLIF_ONEDASH (CLIF_MAY_ONEDASH|_CLIF_STRICT_ONEDASH) +#define CLIF_OPTARG (0x1000) /* allow missing optarg */ +#define CLIF_ABBREV (0x2000) /* allow long opt abbreviation */ +#define CLIF_SEVERAL (0x4000) /* several args in one opt`s arg */ + +/* affected only by common `parse_flags' arg of CLIF_parse_cmdline() . */ +#define CLIF_HELP_EMPTY (0x10000) /* print help on empty cmdline */ +#define CLIF_POSIX (0x20000) /* follow POSIX standard */ +#define CLIF_FIRST_GROUP (0x40000) /* first arg - options` group */ +#define CLIF_STRICT_EXCL (0x80000) /* at least one exclusive */ +#define CLIF_SILENT (0x100000) /* no errors on stderr */ + +#define CLIF_MIN_ABBREV 2 /* a minimal match length in abbrev */ + + +extern int CLIF_parse (int argc, char **argv, CLIF_option *option_list, + CLIF_argument *arg_list, unsigned int parse_flags); +/* history compatibility... */ +#define CLIF_parse_cmdline(ARGC,ARGV,OPTN,ARGS,FLAGS) \ + CLIF_parse (ARGC, ARGV, OPTN, ARGS, FLAGS) + +extern void CLIF_print_options (const char *header, + const CLIF_option *option_list); +extern void CLIF_print_arguments (const char *header, + const CLIF_argument *argument_list); +extern void CLIF_print_usage (const char *header, const char *progname, + const CLIF_option *option_list, + const CLIF_argument *argument_list); + +extern int CLIF_current_help (void); + +/* Common useful option handlers. */ +extern int CLIF_version_handler (CLIF_option *optn, char *arg); +extern int CLIF_set_flag (CLIF_option *optn, char *arg); +extern int CLIF_unset_flag (CLIF_option *optn, char *arg); +extern int CLIF_set_string (CLIF_option *optn, char *arg); +extern int CLIF_set_int (CLIF_option *optn, char *arg); +extern int CLIF_set_uint (CLIF_option *optn, char *arg); +extern int CLIF_set_double (CLIF_option *optn, char *arg); +extern int CLIF_call_func (CLIF_option *optn, char *arg); + +extern int CLIF_arg_string (CLIF_argument *argm, char *arg, int index); +extern int CLIF_arg_int (CLIF_argument *argm, char *arg, int index); +extern int CLIF_arg_uint (CLIF_argument *argm, char *arg, int index); +extern int CLIF_arg_double (CLIF_argument *argm, char *arg, int index); +extern int CLIF_arg_func (CLIF_argument *argm, char *arg, int index); + + +/* Some useful macros. */ + +#define CLIF_HELP_OPTION \ + { 0, "help", 0, "Read this help and exit", \ + CLIF_call_func, CLIF_current_help, 0, CLIF_EXTRA | CLIF_EXIT } +#define CLIF_VERSION_OPTION(STR) \ + { "V", "version", 0, "Print version info and exit", \ + CLIF_version_handler, STR, 0, CLIF_EXTRA | CLIF_EXIT } + +#endif /* _CLIF_H */ diff --git a/store.sh b/store.sh new file mode 100755 index 0000000..0e0c9d0 --- /dev/null +++ b/store.sh @@ -0,0 +1,72 @@ +#!/bin/sh +# +# Copyright (c) 2000, 2001 Dmitry Butskoy +# +# License: GPL v2 or any later +# +# See COPYING for the status of this software. +# + +# +# Store package script. +# Normally invoked by a Makefile. +# +# Stores package with an appropriate version info +# as the main directory postfix (i.e., name-0.1.2/*), +# even if the source dir don`t have it. +# + + +[ $# -lt 2 ] && { + echo "Usage: $0 target store_dir" >&2 + exit 2 +} + + +target=$1 +store_dir=$2 +main_dir=`basename \`pwd\`` + + +# Find current version info. + +dir_v="" +file_v="" + +dir_v=`expr $main_dir : '.*-\([0-9.]*\)$'` +[ -r VERSION ] && file_v=`awk '/^ *#/ { print $3 ; exit; } + /^ *VERSION *=/ {split ($0, a, "="); print a[2]; exit; } + { print $0; exit; }' < VERSION ` +[ -n "$file_v" ] && file_v=`echo $file_v ` # to strip possible spaces + +[ -z "$file_v" -a -z "$dir_v" ] && { + echo "$0: Cannot determine version (use dirname postfix or VERSION file)" >&2 + exit 2 +} + +[ -n "$file_v" -a -n "$dir_v" -a "$dir_v" != "$file_v" ] && { + echo "$0: Different version from dirname postfix and VERSION file" >&2 + exit 2 +} + +version="$dir_v" +[ -z "$version" ] && version="$file_v" + + +targ_vers=${target}-$version + +cd .. + +[ "$main_dir" != "$targ_vers" ] && { + ln -s "$main_dir" "${main_dir}~" || exit 1 # paranoia and paranoia + mv -f "$main_dir" "$targ_vers" || exit 1 +} + +tar -cvhf - "$targ_vers" | gzip -c -9 > $store_dir/${targ_vers}.tar.gz + +[ "$main_dir" != "$targ_vers" ] && { + mv -f "$targ_vers" "$main_dir" + rm -f "${main_dir}~" +} + +exit 0 diff --git a/traceroute.spec b/traceroute.spec new file mode 100644 index 0000000..48d8be0 --- /dev/null +++ b/traceroute.spec @@ -0,0 +1,59 @@ +Summary: Traces the route taken by packets over an IPv4/IPv6 network +Name: traceroute +Version: 2.1.2 +Release: 1%{?dist} +Group: Applications/Internet +License: GPLv2+ +URL: http://traceroute.sourceforge.net +Source0: http://dl.sourceforge.net/traceroute/traceroute-%{version}.tar.gz +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) + + +%description +The traceroute utility displays the route used by IP packets on their +way to a specified network (or Internet) host. Traceroute displays +the IP number and host name (if possible) of the machines along the +route taken by the packets. Traceroute is used as a network debugging +tool. If you're having network connectivity problems, traceroute will +show you where the trouble is coming from along the route. + +Install traceroute if you need a tool for diagnosing network connectivity +problems. + + +%prep +%setup -q + + +%build +make %{?_smp_mflags} CFLAGS="$RPM_OPT_FLAGS" LDFLAGS="" + + +%install +rm -rf $RPM_BUILD_ROOT + +install -d $RPM_BUILD_ROOT/bin +install -m755 traceroute/traceroute $RPM_BUILD_ROOT/bin +pushd $RPM_BUILD_ROOT/bin +ln -s traceroute traceroute6 +popd + +install -d $RPM_BUILD_ROOT%{_mandir}/man8 +install -p -m644 traceroute/traceroute.8 $RPM_BUILD_ROOT%{_mandir}/man8 +ln -s traceroute.8 $RPM_BUILD_ROOT%{_mandir}/man8/traceroute6.8 + + +%clean +rm -rf $RPM_BUILD_ROOT + + +%files +%defattr(-,root,root,-) +%doc COPYING README TODO CREDITS +/bin/* +%{_mandir}/*/* + + +%changelog +* Tue Oct 20 2006 Dmitry Butskoy - 2.0.2-1 +- initial release diff --git a/traceroute/as_lookups.c b/traceroute/as_lookups.c new file mode 100644 index 0000000..aedf7e1 --- /dev/null +++ b/traceroute/as_lookups.c @@ -0,0 +1,128 @@ +/* + Copyright (c) 2006, 2007 Dmitry Butskoy + + License: GPL v2 or any later + + See COPYING for the status of this software. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "traceroute.h" + + +#define DEF_RADB_SERVER "whois.radb.net" +#define DEF_RADB_SERVICE "nicname" + + +static sockaddr_any ra_addr = {{ 0, }, }; +static char ra_buf[512] = { 0, }; + + +const char *get_as_path (const char *query) { + int sk, n; + FILE *fp; + char buf[1024]; + int prefix = 0, best_prefix = 0; + char *rb, *re = &ra_buf[sizeof (ra_buf) / sizeof (*ra_buf) - 1]; + + + if (!ra_addr.sa.sa_family) { + const char *server, *service; + struct addrinfo *res; + int ret; + + server = getenv ("RA_SERVER"); + if (!server) server = DEF_RADB_SERVER; + + service = getenv ("RA_SERVICE"); + if (!service) service = DEF_RADB_SERVICE; + + + ret = getaddrinfo (server, service, NULL, &res); + if (ret) { + fprintf (stderr, "%s/%s: %s\n", server, service, + gai_strerror(ret)); + exit (2); + } + + memcpy (&ra_addr, res->ai_addr, res->ai_addrlen); + + freeaddrinfo (res); + } + + + sk = socket (ra_addr.sa.sa_family, SOCK_STREAM, 0); + if (sk < 0) error ("socket"); + + if (connect (sk, &ra_addr.sa, sizeof (ra_addr)) < 0) + goto err_sk; + + n = snprintf (buf, sizeof (buf), "%s\r\n", query); + if (n >= sizeof (buf)) goto err_sk; + + if (write (sk, buf, n) < n) + goto err_sk; + + fp = fdopen (sk, "r"); + if (!fp) goto err_sk; + + + strcpy (ra_buf, "*"); + rb = ra_buf; + + while (fgets (buf, sizeof (buf), fp) != NULL) { + + if (!strncmp (buf, "route:", sizeof ("route:") - 1) || + !strncmp (buf, "route6:", sizeof ("route6:") - 1) + ) { + char *p = strchr (buf, '/'); + + if (p) prefix = strtoul (++p, NULL, 10); + else prefix = 0; /* Hmmm... */ + + } + else if (!strncmp (buf, "origin:", sizeof ("origin:") -1)) { + char *p, *as; + + p = buf + (sizeof ("origin:") - 1); + + while (isspace (*p)) p++; + as = p; + while (*p && !isspace (*p)) p++; + *p = '\0'; + + if (prefix > best_prefix) { + best_prefix = prefix; + + rb = ra_buf; + while (rb < re && (*rb++ = *as++)) ; + } + else if (prefix == best_prefix) { + char *q = strstr (ra_buf, as); + + if (!q || (*(q += strlen (as)) != '\0' && *q != '/')) { + if (rb > ra_buf) rb[-1] = '/'; + while (rb < re && (*rb++ = *as++)) ; + } + } + /* else just ignore it */ + } + } + + fclose (fp); + + return ra_buf; + + +err_sk: + close (sk); + return "!!"; +} diff --git a/traceroute/csum.c b/traceroute/csum.c new file mode 100644 index 0000000..ea3a50d --- /dev/null +++ b/traceroute/csum.c @@ -0,0 +1,34 @@ +/* + Copyright (c) 2006, 2007 Dmitry Butskoy + + License: GPL v2 or any later + + See COPYING for the status of this software. +*/ + +#include +#include + +#include "traceroute.h" + + +uint16_t in_csum (const void *ptr, size_t len) { + const uint16_t *p = (const uint16_t *) ptr; + size_t nw = len / 2; + unsigned int sum = 0; + uint16_t res; + + while (nw--) sum += *p++; + + if (len & 0x1) + sum += htons (*((unsigned char *) p) << 8); + + sum = (sum >> 16) + (sum & 0xffff); + sum += (sum >> 16); + + res = ~sum; + if (!res) res = ~0; + + return res; +} + diff --git a/traceroute/extension.c b/traceroute/extension.c new file mode 100644 index 0000000..515a8db --- /dev/null +++ b/traceroute/extension.c @@ -0,0 +1,132 @@ +#include +#include +#include +#include + +#include "traceroute.h" + + +struct icmp_ext_header { +#if __BYTE_ORDER == __BIG_ENDIAN + unsigned int version:4; + unsigned int reserved:4; +#else + unsigned int reserved:4; + unsigned int version:4; +#endif + uint8_t reserved1; + uint16_t checksum; +} __attribute__ ((packed)); + + +struct icmp_ext_object { + uint16_t length; + uint8_t class; + uint8_t c_type; + uint8_t data[0]; +}; + +#define MPLS_CLASS 1 +#define MPLS_C_TYPE 1 + + +#define do_snprintf(CURR, END, FMT, ARGS...) \ + do { \ + CURR += snprintf (CURR, END - CURR, (FMT), ## ARGS);\ + if (CURR > END) CURR = END; \ + } while (0) + + +static int try_extension (probe *pb, char *buf, size_t len) { + struct icmp_ext_header *iext = (struct icmp_ext_header *) buf; + char str[1024]; + char *curr = str; + char *end = str + sizeof (str) / sizeof (*str); + + + /* a check for len >= 8 already done for all cases */ + + if (iext->version != 2) return -1; + + if (iext->checksum && + in_csum (iext, len) != (uint16_t) ~0 + ) return -1; + + buf += sizeof (*iext); + len -= sizeof (*iext); + + + while (len >= sizeof (struct icmp_ext_object)) { + struct icmp_ext_object *obj = (struct icmp_ext_object *) buf; + size_t objlen = ntohs (obj->length); + size_t data_len; + uint32_t *ui = (uint32_t *) obj->data; + int i, n; + + if (objlen < sizeof (*obj) || + objlen > len + ) return -1; + + data_len = objlen - sizeof (*obj); + if (data_len % sizeof (uint32_t)) + return -1; /* must be 32bit rounded... */ + + n = data_len / sizeof (*ui); + + + if (curr > (char *) str && curr < end) + *curr++ = ';'; /* a separator */ + + if (obj->class == MPLS_CLASS && + obj->c_type == MPLS_C_TYPE && + n >= 1 + ) { /* people prefer MPLS to be parsed... */ + + do_snprintf (curr, end, "MPLS:"); + + for (i = 0; i < n; i++, ui++) { + uint32_t mpls = ntohl (*ui); + + do_snprintf (curr, end, "%sL=%u,E=%u,S=%u,T=%u", + i ? "/" : "", + mpls >> 12, + (mpls >> 9) & 0x7, + (mpls >> 8) & 0x1, + mpls & 0xff); + } + + } + else { /* common case... */ + + do_snprintf (curr, end, "%u/%u:", obj->class, obj->c_type); + + for (i = 0; i < n && curr < end; i++, ui++) + do_snprintf (curr, end, "%s%08x", i ? "," : "", ntohl(*ui)); + } + + buf += objlen; + len -= objlen; + } + + if (len) return -1; + + + pb->ext = strdup (str); + + return 0; +} + + +void handle_extensions (probe *pb, char *buf, int len, int step) { + + if (!step) + try_extension (pb, buf, len); + else { + for ( ; len >= 8; buf += step, len -= step) + if (try_extension (pb, buf, len) == 0) + break; + } + + return; +} + diff --git a/traceroute/flowlabel.h b/traceroute/flowlabel.h new file mode 100644 index 0000000..af5a65a --- /dev/null +++ b/traceroute/flowlabel.h @@ -0,0 +1,40 @@ +/* + It is just a stripped copy of the kernel header "linux/in6.h" + + "Flow label" things are still not defined in "netinet/in*.h" headers, + but we cannot use "linux/in6.h" immediately because it currently + conflicts with "netinet/in.h" . +*/ + +struct in6_flowlabel_req +{ + struct in6_addr flr_dst; + __u32 flr_label; + __u8 flr_action; + __u8 flr_share; + __u16 flr_flags; + __u16 flr_expires; + __u16 flr_linger; + __u32 __flr_pad; + /* Options in format of IPV6_PKTOPTIONS */ +}; + +#define IPV6_FL_A_GET 0 +#define IPV6_FL_A_PUT 1 +#define IPV6_FL_A_RENEW 2 + +#define IPV6_FL_F_CREATE 1 +#define IPV6_FL_F_EXCL 2 + +#define IPV6_FL_S_NONE 0 +#define IPV6_FL_S_EXCL 1 +#define IPV6_FL_S_PROCESS 2 +#define IPV6_FL_S_USER 3 +#define IPV6_FL_S_ANY 255 + +#define IPV6_FLOWINFO_FLOWLABEL 0x000fffff +#define IPV6_FLOWINFO_PRIORITY 0x0ff00000 + +#define IPV6_FLOWLABEL_MGR 32 +#define IPV6_FLOWINFO_SEND 33 + diff --git a/traceroute/mod-dccp.c b/traceroute/mod-dccp.c new file mode 100644 index 0000000..5d8518b --- /dev/null +++ b/traceroute/mod-dccp.c @@ -0,0 +1,289 @@ +/* + Copyright (c) 2012 Samuel Jero + License: GPL v2 or any later + + See COPYING for the status of this software. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "traceroute.h" + + +#define DEF_SERVICE_CODE 1885957735 + +#define DCCP_HEADER_LEN (sizeof (struct dccp_hdr) + \ + sizeof (struct dccp_hdr_ext) \ + + sizeof (struct dccp_hdr_request)) + +static sockaddr_any dest_addr = {{ 0, }, }; +static unsigned int dest_port = 0; + +static int raw_sk = -1; +static int last_ttl = 0; + +static uint8_t buf[1024]; /* enough, enough... */ +static size_t csum_len = 0; +static struct dccp_hdr *dh = NULL; +static struct dccp_hdr_ext *dhe = NULL; +static struct dccp_hdr_request *dhr = NULL; +static unsigned int service_code = DEF_SERVICE_CODE; + + +static CLIF_option dccp_options[] = { + { 0, "service", "NUM", "Set DCCP service code to %s (default is " + _TEXT (DEF_SERVICE_CODE) ")", + CLIF_set_uint, &service_code, 0, CLIF_ABBREV }, + CLIF_END_OPTION +}; + + +static int dccp_init (const sockaddr_any *dest, + unsigned int port_seq, size_t *packet_len_p) { + int af = dest->sa.sa_family; + sockaddr_any src; + socklen_t len; + uint8_t *ptr; + uint16_t *lenp; + + + dest_addr = *dest; + dest_addr.sin.sin_port = 0; /* raw sockets can be confused */ + + if (!port_seq) port_seq = DEF_DCCP_PORT; + dest_port = htons (port_seq); + + + /* Create raw socket for DCCP */ + raw_sk = socket (af, SOCK_RAW, IPPROTO_DCCP); + if (raw_sk < 0) + error_or_perm ("socket"); + + tune_socket (raw_sk); /* including bind, if any */ + + if (connect (raw_sk, &dest_addr.sa, sizeof (dest_addr)) < 0) + error ("connect"); + + len = sizeof (src); + if (getsockname (raw_sk, &src.sa, &len) < 0) + error ("getsockname"); + + + if (!raw_can_connect ()) { /* work-around for buggy kernels */ + close (raw_sk); + raw_sk = socket (af, SOCK_RAW, IPPROTO_DCCP); + if (raw_sk < 0) error ("socket"); + tune_socket (raw_sk); + /* but do not connect it... */ + } + + + use_recverr (raw_sk); + + add_poll (raw_sk, POLLIN | POLLERR); + + + /* Now create the sample packet. */ + + /* For easy checksum computing: + saddr + daddr + length + protocol + dccphdr + */ + + ptr = buf; + + if (af == AF_INET) { + len = sizeof (src.sin.sin_addr); + memcpy (ptr, &src.sin.sin_addr, len); + ptr += len; + memcpy (ptr, &dest_addr.sin.sin_addr, len); + ptr += len; + } else { + len = sizeof (src.sin6.sin6_addr); + memcpy (ptr, &src.sin6.sin6_addr, len); + ptr += len; + memcpy (ptr, &dest_addr.sin6.sin6_addr, len); + ptr += len; + } + + lenp = (uint16_t *) ptr; + ptr += sizeof (uint16_t); + *((uint16_t *) ptr) = htons ((uint16_t) IPPROTO_DCCP); + ptr += sizeof (uint16_t); + + + /* Construct DCCP header */ + dh = (struct dccp_hdr *) ptr; + + dh->dccph_ccval = 0; + dh->dccph_checksum = 0; + dh->dccph_cscov = 0; + dh->dccph_dport = dest_port; + dh->dccph_reserved = 0; + dh->dccph_sport = 0; /* temporary */ + dh->dccph_x = 1; + dh->dccph_type = DCCP_PKT_REQUEST; + dh->dccph_seq2 = 0; /* reserved if using 48 bit sequence numbers */ + /* high 16 bits of sequence number. Always make 0 for simplicity. */ + dh->dccph_seq = 0; + ptr += sizeof (struct dccp_hdr); + + dhe = (struct dccp_hdr_ext *) ptr; + dhe->dccph_seq_low = 0; /* temporary */ + ptr += sizeof (struct dccp_hdr_ext); + + dhr = (struct dccp_hdr_request *) ptr; + dhr->dccph_req_service = htonl (service_code); + ptr += sizeof (struct dccp_hdr_request); + + + csum_len = ptr - buf; + + if (csum_len > sizeof (buf)) + error ("impossible"); /* paranoia */ + + len = ptr - (uint8_t *) dh; + if (len & 0x03) error ("impossible"); /* as >>2 ... */ + + *lenp = htons (len); + dh->dccph_doff = len >> 2; + + + *packet_len_p = len; + + return 0; +} + + +static void dccp_send_probe (probe *pb, int ttl) { + int sk; + int af = dest_addr.sa.sa_family; + sockaddr_any addr; + socklen_t len = sizeof (addr); + + + /* To make sure we have chosen a free unused "source port", + just create, (auto)bind and hold a socket while the port is needed. + */ + + sk = socket (af, SOCK_DCCP, IPPROTO_DCCP); + if (sk < 0) error ("socket"); + + bind_socket (sk); + + if (getsockname (sk, &addr.sa, &len) < 0) + error ("getsockname"); + + /* When we reach the target host, it can send us either Reset or Response. + For Reset all is OK (we and kernel just answer nothing), but + for Response we should reply with our Close. + It is well-known "half-open technique", used by port scanners etc. + This way we do not touch remote applications at all, unlike + the ordinary connect(2) call. + As the port-holding socket neither connect() nor listen(), + it means "no such port yet" for remote ends, and kernel always + send Reset in such a situation automatically (we have to do nothing). + */ + + + dh->dccph_sport = addr.sin.sin_port; + + dhe->dccph_seq_low = random_seq (); + + dh->dccph_checksum = 0; + dh->dccph_checksum = in_csum (buf, csum_len); + + + if (ttl != last_ttl) { + set_ttl (raw_sk, ttl); + last_ttl = ttl; + } + + + pb->send_time = get_time (); + + if (do_send (raw_sk, dh, dh->dccph_doff << 2, &dest_addr) < 0) { + close (sk); + pb->send_time = 0; + return; + } + + + pb->seq = dh->dccph_sport; + + pb->sk = sk; + + return; +} + + +static probe *dccp_check_reply (int sk, int err, sockaddr_any *from, + char *buf, size_t len) { + probe *pb; + struct dccp_hdr *ndh = (struct dccp_hdr *) buf; + uint16_t sport, dport; + + + if (len < 8) return NULL; /* too short */ + + + if (err) { + sport = ndh->dccph_sport; + dport = ndh->dccph_dport; + } else { + sport = ndh->dccph_dport; + dport = ndh->dccph_sport; + } + + + if (dport != dest_port) + return NULL; + + if (!equal_addr (&dest_addr, from)) + return NULL; + + pb = probe_by_seq (sport); + if (!pb) return NULL; + + if (!err) pb->final = 1; + + return pb; +} + + +static void dccp_recv_probe (int sk, int revents) { + + if (!(revents & (POLLIN | POLLERR))) + return; + + recv_reply (sk, !!(revents & POLLERR), dccp_check_reply); +} + + +static void dccp_expire_probe (probe *pb) { + + probe_done (pb); +} + + +static tr_module dccp_ops = { + .name = "dccp", + .init = dccp_init, + .send_probe = dccp_send_probe, + .recv_probe = dccp_recv_probe, + .expire_probe = dccp_expire_probe, + .options = dccp_options, +}; + +TR_MODULE (dccp_ops); diff --git a/traceroute/mod-icmp.c b/traceroute/mod-icmp.c new file mode 100644 index 0000000..f46c808 --- /dev/null +++ b/traceroute/mod-icmp.c @@ -0,0 +1,247 @@ +/* + Copyright (c) 2006, 2007 Dmitry Butskoy + + License: GPL v2 or any later + + See COPYING for the status of this software. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "traceroute.h" + + +static sockaddr_any dest_addr = {{ 0, }, }; +static uint16_t seq = 1; +static uint16_t ident = 0; + +static char *data; +static size_t *length_p; + +static int icmp_sk = -1; +static int last_ttl = 0; + +static int raw = 0; +static int dgram = 0; + + +static CLIF_option icmp_options[] = { + { 0, "raw", 0, "Use raw sockets way only. Default is try this way " + "first (probably not allowed for unprivileged users), " + "then try dgram", + CLIF_set_flag, &raw, 0, CLIF_EXCL }, + { 0, "dgram", 0, "Use dgram sockets way only. May be not implemented " + "by old kernels or restricted by sysadmins", + CLIF_set_flag, &dgram, 0, CLIF_EXCL }, + CLIF_END_OPTION +}; + + +static int icmp_init (const sockaddr_any *dest, + unsigned int port_seq, size_t *packet_len_p) { + int i; + int af = dest->sa.sa_family; + int protocol; + + dest_addr = *dest; + dest_addr.sin.sin_port = 0; + + if (port_seq) seq = port_seq; + + length_p = packet_len_p; + if (*length_p < sizeof (struct icmphdr)) + *length_p = sizeof (struct icmphdr); + + data = malloc (*length_p); + if (!data) error ("malloc"); + + for (i = sizeof (struct icmphdr); i < *length_p; i++) + data[i] = 0x40 + (i & 0x3f); + + + protocol = (af == AF_INET) ? IPPROTO_ICMP : IPPROTO_ICMPV6; + + if (!raw) { + icmp_sk = socket (af, SOCK_DGRAM, protocol); + if (icmp_sk < 0 && dgram) + error ("socket"); + } + + if (!dgram) { + int raw_sk = socket (af, SOCK_RAW, protocol); + if (raw_sk < 0) { + if (raw || icmp_sk < 0) + error_or_perm ("socket"); + dgram = 1; + } else { + /* prefer the traditional "raw" way when possible */ + close (icmp_sk); + icmp_sk = raw_sk; + } + } + + + tune_socket (icmp_sk); + + /* Don't want to catch packets from another hosts */ + if (raw_can_connect () && + connect (icmp_sk, &dest_addr.sa, sizeof (dest_addr)) < 0 + ) error ("connect"); + + use_recverr (icmp_sk); + + + if (dgram) { + sockaddr_any addr; + socklen_t len = sizeof (addr); + + if (getsockname (icmp_sk, &addr.sa, &len) < 0) + error ("getsockname"); + ident = ntohs (addr.sin.sin_port); /* both IPv4 and IPv6 */ + + } else + ident = getpid () & 0xffff; + + + add_poll (icmp_sk, POLLIN | POLLERR); + + return 0; +} + + +static void icmp_send_probe (probe *pb, int ttl) { + int af = dest_addr.sa.sa_family; + + + if (ttl != last_ttl) { + + set_ttl (icmp_sk, ttl); + + last_ttl = ttl; + } + + + if (af == AF_INET) { + struct icmp *icmp = (struct icmp *) data; + + icmp->icmp_type = ICMP_ECHO; + icmp->icmp_code = 0; + icmp->icmp_cksum = 0; + icmp->icmp_id = htons (ident); + icmp->icmp_seq = htons (seq); + + icmp->icmp_cksum = in_csum (data, *length_p); + } + else if (af == AF_INET6) { + struct icmp6_hdr *icmp6 = (struct icmp6_hdr *) data; + + icmp6->icmp6_type = ICMP6_ECHO_REQUEST; + icmp6->icmp6_code = 0; + icmp6->icmp6_cksum = 0; + icmp6->icmp6_id = htons (ident); + icmp6->icmp6_seq = htons(seq); + + /* icmp6->icmp6_cksum always computed by kernel internally */ + } + + + pb->send_time = get_time (); + + if (do_send (icmp_sk, data, *length_p, &dest_addr) < 0) { + pb->send_time = 0; + return; + } + + + pb->seq = seq; + + seq++; + + return; +} + + +static probe *icmp_check_reply (int sk, int err, sockaddr_any *from, + char *buf, size_t len) { + int af = dest_addr.sa.sa_family; + int type; + uint16_t recv_id, recv_seq; + probe *pb; + + + if (len < sizeof (struct icmphdr)) + return NULL; + + + if (af == AF_INET) { + struct icmp *icmp = (struct icmp *) buf; + + type = icmp->icmp_type; + + recv_id = ntohs (icmp->icmp_id); + recv_seq = ntohs (icmp->icmp_seq); + + } + else { /* AF_INET6 */ + struct icmp6_hdr *icmp6 = (struct icmp6_hdr *) buf; + + type = icmp6->icmp6_type; + + recv_id = ntohs (icmp6->icmp6_id); + recv_seq = ntohs (icmp6->icmp6_seq); + } + + + if (recv_id != ident) + return NULL; + + pb = probe_by_seq (recv_seq); + if (!pb) return NULL; + + + if (!err) { + + if (!(af == AF_INET && type == ICMP_ECHOREPLY) && + !(af == AF_INET6 && type == ICMP6_ECHO_REPLY) + ) return NULL; + + pb->final = 1; + } + + return pb; +} + + +static void icmp_recv_probe (int sk, int revents) { + + if (!(revents & (POLLIN | POLLERR))) + return; + + recv_reply (sk, !!(revents & POLLERR), icmp_check_reply); +} + + +static void icmp_expire_probe (probe *pb) { + + probe_done (pb); +} + + +static tr_module icmp_ops = { + .name = "icmp", + .init = icmp_init, + .send_probe = icmp_send_probe, + .recv_probe = icmp_recv_probe, + .expire_probe = icmp_expire_probe, + .options = icmp_options, +}; + +TR_MODULE (icmp_ops); diff --git a/traceroute/mod-raw.c b/traceroute/mod-raw.c new file mode 100644 index 0000000..26a6cec --- /dev/null +++ b/traceroute/mod-raw.c @@ -0,0 +1,163 @@ +/* + Copyright (c) 2006, 2007 Dmitry Butskoy + + License: GPL v2 or any later + + See COPYING for the status of this software. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "traceroute.h" + + +static sockaddr_any dest_addr = {{ 0, }, }; +static int protocol = DEF_RAW_PROT; + +static char *data = NULL; +static size_t *length_p; + +static int raw_sk = -1; +static int last_ttl = 0; +static int seq = 0; + + +static int set_protocol (CLIF_option *optn, char *arg) { + char *q; + + protocol = strtoul (arg, &q, 0); + if (q == arg) { + struct protoent *p = getprotobyname (arg); + + if (!p) return -1; + protocol = p->p_proto; + } + + return 0; +} + + +static CLIF_option raw_options[] = { + { 0, "protocol", "PROT", "Use protocol %s (default is " + _TEXT (DEF_RAW_PROT) ")", + set_protocol, 0, 0, CLIF_ABBREV }, + CLIF_END_OPTION +}; + + +static int raw_init (const sockaddr_any *dest, + unsigned int port_seq, size_t *packet_len_p) { + int i; + int af = dest->sa.sa_family; + + dest_addr = *dest; + dest_addr.sin.sin_port = 0; + + if (port_seq) protocol = port_seq; + + + length_p = packet_len_p; + + if (*length_p && + !(data = malloc (*length_p)) + ) error ("malloc"); + + for (i = 0; i < *length_p; i++) + data[i] = 0x40 + (i & 0x3f); + + + raw_sk = socket (af, SOCK_RAW, protocol); + if (raw_sk < 0) + error_or_perm ("socket"); + + tune_socket (raw_sk); + + /* Don't want to catch packets from another hosts */ + if (raw_can_connect () && + connect (raw_sk, &dest_addr.sa, sizeof (dest_addr)) < 0 + ) error ("connect"); + + use_recverr (raw_sk); + + + add_poll (raw_sk, POLLIN | POLLERR); + + return 0; +} + + +static void raw_send_probe (probe *pb, int ttl) { + + if (ttl != last_ttl) { + + set_ttl (raw_sk, ttl); + + last_ttl = ttl; + } + + + pb->send_time = get_time (); + + if (do_send (raw_sk, data, *length_p, &dest_addr) < 0) { + pb->send_time = 0; + return; + } + + + pb->seq = ++seq; + + return; +} + + +static probe *raw_check_reply (int sk, int err, sockaddr_any *from, + char *buf, size_t len) { + probe *pb; + + if (!equal_addr (&dest_addr, from)) + return NULL; + + pb = probe_by_seq (seq); + if (!pb) return NULL; + + if (!err) pb->final = 1; + + return pb; +} + + +static void raw_recv_probe (int sk, int revents) { + + if (!(revents & (POLLIN | POLLERR))) + return; + + recv_reply (sk, !!(revents & POLLERR), raw_check_reply); +} + + +static void raw_expire_probe (probe *pb) { + + probe_done (pb); +} + + +static tr_module raw_ops = { + .name = "raw", + .init = raw_init, + .send_probe = raw_send_probe, + .recv_probe = raw_recv_probe, + .expire_probe = raw_expire_probe, + .options = raw_options, + .one_per_time = 1, +}; + +TR_MODULE (raw_ops); diff --git a/traceroute/mod-tcp.c b/traceroute/mod-tcp.c new file mode 100644 index 0000000..a6b9ffa --- /dev/null +++ b/traceroute/mod-tcp.c @@ -0,0 +1,507 @@ +/* + Copyright (c) 2006, 2007 Dmitry Butskoy + + License: GPL v2 or any later + + See COPYING for the status of this software. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "traceroute.h" + + +#ifndef IP_MTU +#define IP_MTU 14 +#endif + + +static sockaddr_any dest_addr = {{ 0, }, }; +static unsigned int dest_port = 0; + +static int raw_sk = -1; +static int last_ttl = 0; + +static uint8_t buf[1024]; /* enough, enough... */ +static size_t csum_len = 0; +static struct tcphdr *th = NULL; + +#define TH_FLAGS(TH) (((uint8_t *) (TH))[13]) +#define TH_FIN 0x01 +#define TH_SYN 0x02 +#define TH_RST 0x04 +#define TH_PSH 0x08 +#define TH_ACK 0x10 +#define TH_URG 0x20 +#define TH_ECE 0x40 +#define TH_CWR 0x80 + + +static int flags = 0; /* & 0xff == tcp_flags ... */ +static int sysctl = 0; +static int reuse = 0; +static unsigned int mss = 0; +static int info = 0; + +#define FL_FLAGS 0x0100 +#define FL_ECN 0x0200 +#define FL_SACK 0x0400 +#define FL_TSTAMP 0x0800 +#define FL_WSCALE 0x1000 + + +static struct { + const char *name; + unsigned int flag; +} tcp_flags[] = { + { "fin", TH_FIN }, + { "syn", TH_SYN }, + { "rst", TH_RST }, + { "psh", TH_PSH }, + { "ack", TH_ACK }, + { "urg", TH_URG }, + { "ece", TH_ECE }, + { "cwr", TH_CWR }, +}; + +static char *names_by_flags (unsigned int flags) { + int i; + char str[64]; /* enough... */ + char *curr = str; + char *end = str + sizeof (str) / sizeof (*str); + + for (i = 0; i < sizeof (tcp_flags) / sizeof (*tcp_flags); i++) { + const char *p; + + if (!(flags & tcp_flags[i].flag)) continue; + + if (curr > str && curr < end) *curr++ = ','; + for (p = tcp_flags[i].name; *p && curr < end; *curr++ = *p++) ; + } + + *curr = '\0'; + + return strdup (str); +} + +static int set_tcp_flag (CLIF_option *optn, char *arg) { + int i; + + for (i = 0; i < sizeof (tcp_flags) / sizeof (*tcp_flags); i++) { + if (!strcmp (optn->long_opt, tcp_flags[i].name)) { + flags |= tcp_flags[i].flag; + return 0; + } + } + + return -1; +} + +static int set_tcp_flags (CLIF_option *optn, char *arg) { + char *q; + unsigned long value; + + value = strtoul (arg, &q, 0); + if (q == arg) return -1; + + flags = (flags & ~0xff) | (value & 0xff) | FL_FLAGS; + return 0; +} + +static int set_flag (CLIF_option *optn, char *arg) { + + flags |= (unsigned long) optn->data; + + return 0; +} + +static CLIF_option tcp_options[] = { + { 0, "syn", 0, "Set tcp flag SYN (default if no other " + "tcp flags specified)", set_tcp_flag, 0, 0, 0 }, + { 0, "ack", 0, "Set tcp flag ACK,", set_tcp_flag, 0, 0, 0 }, + { 0, "fin", 0, "FIN,", set_tcp_flag, 0, 0, 0 }, + { 0, "rst", 0, "RST,", set_tcp_flag, 0, 0, 0 }, + { 0, "psh", 0, "PSH,", set_tcp_flag, 0, 0, 0 }, + { 0, "urg", 0, "URG,", set_tcp_flag, 0, 0, 0 }, + { 0, "ece", 0, "ECE,", set_tcp_flag, 0, 0, 0 }, + { 0, "cwr", 0, "CWR", set_tcp_flag, 0, 0, 0 }, + { 0, "flags", "NUM", "Set tcp flags exactly to value %s", + set_tcp_flags, 0, 0, CLIF_ABBREV }, + { 0, "ecn", 0, "Send syn packet with tcp flags ECE and CWR " + "(for Explicit Congestion Notification, rfc3168)", + set_flag, (void *) FL_ECN, 0, 0 }, + { 0, "sack", 0, "Use sack,", + set_flag, (void *) FL_SACK, 0, 0 }, + { 0, "timestamps", 0, "timestamps,", + set_flag, (void *) FL_TSTAMP, 0, CLIF_ABBREV }, + { 0, "window_scaling", 0, "window_scaling option for tcp", + set_flag, (void *) FL_WSCALE, 0, CLIF_ABBREV }, + { 0, "sysctl", 0, "Use current sysctl (/proc/sys/net/*) setting " + "for the tcp options and ecn. Always set by default " + "(with \"syn\") if nothing else specified", + CLIF_set_flag, &sysctl, 0, 0 }, + { 0, "reuse", 0, "Allow to reuse local port numbers " + "for the huge workloads (SO_REUSEADDR)", + CLIF_set_flag, &reuse, 0, 0 }, + { 0, "mss", "NUM", "Use value of %s for maxseg tcp option (when syn)", + CLIF_set_uint, &mss, 0, 0 }, + { 0, "info", 0, "Print tcp flags of final tcp replies when target " + "host is reached. Useful to determine whether " + "an application listens the port etc.", + CLIF_set_flag, &info, 0, 0 }, + CLIF_END_OPTION +}; + + +#define SYSCTL_PREFIX "/proc/sys/net/ipv4/tcp_" +static int check_sysctl (const char *name) { + int fd, res; + char buf[sizeof (SYSCTL_PREFIX) + strlen (name) + 1]; + uint8_t ch; + + strcpy (buf, SYSCTL_PREFIX); + strcat (buf, name); + + fd = open (buf, O_RDONLY, 0); + if (fd < 0) return 0; + + res = read (fd, &ch, sizeof (ch)); + close (fd); + + if (res != sizeof (ch)) + return 0; + + /* since kernel 2.6.31 "tcp_ecn" can have value of '2'... */ + if (ch == '1') return 1; + + return 0; +} + + +static int tcp_init (const sockaddr_any *dest, + unsigned int port_seq, size_t *packet_len_p) { + int af = dest->sa.sa_family; + sockaddr_any src; + int mtu; + socklen_t len; + uint8_t *ptr; + uint16_t *lenp; + + + dest_addr = *dest; + dest_addr.sin.sin_port = 0; /* raw sockets can be confused */ + + if (!port_seq) port_seq = DEF_TCP_PORT; + dest_port = htons (port_seq); + + + /* Create raw socket for tcp */ + + raw_sk = socket (af, SOCK_RAW, IPPROTO_TCP); + if (raw_sk < 0) + error_or_perm ("socket"); + + tune_socket (raw_sk); /* including bind, if any */ + + if (connect (raw_sk, &dest_addr.sa, sizeof (dest_addr)) < 0) + error ("connect"); + + len = sizeof (src); + if (getsockname (raw_sk, &src.sa, &len) < 0) + error ("getsockname"); + + + len = sizeof (mtu); + if (getsockopt (raw_sk, af == AF_INET ? SOL_IP : SOL_IPV6, + af == AF_INET ? IP_MTU : IPV6_MTU, + &mtu, &len) < 0 || mtu < 576 + ) mtu = 576; + + /* mss = mtu - headers */ + mtu -= af == AF_INET ? sizeof (struct iphdr) : sizeof (struct ip6_hdr); + mtu -= sizeof (struct tcphdr); + + + if (!raw_can_connect ()) { /* work-around for buggy kernels */ + close (raw_sk); + raw_sk = socket (af, SOCK_RAW, IPPROTO_TCP); + if (raw_sk < 0) error ("socket"); + tune_socket (raw_sk); + /* but do not connect it... */ + } + + + use_recverr (raw_sk); + + add_poll (raw_sk, POLLIN | POLLERR); + + + /* Now create the sample packet. */ + + if (!flags) sysctl = 1; + + if (sysctl) { + if (check_sysctl ("ecn")) flags |= FL_ECN; + if (check_sysctl ("sack")) flags |= FL_SACK; + if (check_sysctl ("timestamps")) flags |= FL_TSTAMP; + if (check_sysctl ("window_scaling")) flags |= FL_WSCALE; + } + + if (!(flags & (FL_FLAGS | 0xff))) { /* no any tcp flag set */ + flags |= TH_SYN; + if (flags & FL_ECN) + flags |= TH_ECE | TH_CWR; + } + + + /* For easy checksum computing: + saddr + daddr + length + protocol + tcphdr + tcpoptions + */ + + ptr = buf; + + if (af == AF_INET) { + len = sizeof (src.sin.sin_addr); + memcpy (ptr, &src.sin.sin_addr, len); + ptr += len; + memcpy (ptr, &dest_addr.sin.sin_addr, len); + ptr += len; + } else { + len = sizeof (src.sin6.sin6_addr); + memcpy (ptr, &src.sin6.sin6_addr, len); + ptr += len; + memcpy (ptr, &dest_addr.sin6.sin6_addr, len); + ptr += len; + } + + lenp = (uint16_t *) ptr; + ptr += sizeof (uint16_t); + *((uint16_t *) ptr) = htons ((uint16_t) IPPROTO_TCP); + ptr += sizeof (uint16_t); + + + /* Construct TCP header */ + + th = (struct tcphdr *) ptr; + + th->source = 0; /* temporary */ + th->dest = dest_port; + th->seq = 0; /* temporary */ + th->ack_seq = 0; + th->doff = 0; /* later... */ + TH_FLAGS(th) = flags & 0xff; + th->window = htons (4 * mtu); + th->check = 0; + th->urg_ptr = 0; + + + /* Build TCP options */ + + ptr = (uint8_t *) (th + 1); + + if (flags & TH_SYN) { + *ptr++ = TCPOPT_MAXSEG; /* 2 */ + *ptr++ = TCPOLEN_MAXSEG; /* 4 */ + *((uint16_t *) ptr) = htons (mss ? mss : mtu); + ptr += sizeof (uint16_t); + } + + if (flags & FL_TSTAMP) { + + if (flags & FL_SACK) { + *ptr++ = TCPOPT_SACK_PERMITTED; /* 4 */ + *ptr++ = TCPOLEN_SACK_PERMITTED;/* 2 */ + } else { + *ptr++ = TCPOPT_NOP; /* 1 */ + *ptr++ = TCPOPT_NOP; /* 1 */ + } + *ptr++ = TCPOPT_TIMESTAMP; /* 8 */ + *ptr++ = TCPOLEN_TIMESTAMP; /* 10 */ + + *((uint32_t *) ptr) = random_seq (); /* really! */ + ptr += sizeof (uint32_t); + *((uint32_t *) ptr) = (flags & TH_ACK) ? random_seq () : 0; + ptr += sizeof (uint32_t); + } + else if (flags & FL_SACK) { + *ptr++ = TCPOPT_NOP; /* 1 */ + *ptr++ = TCPOPT_NOP; /* 1 */ + *ptr++ = TCPOPT_SACK_PERMITTED; /* 4 */ + *ptr++ = TCPOLEN_SACK_PERMITTED; /* 2 */ + } + + if (flags & FL_WSCALE) { + *ptr++ = TCPOPT_NOP; /* 1 */ + *ptr++ = TCPOPT_WINDOW; /* 3 */ + *ptr++ = TCPOLEN_WINDOW; /* 3 */ + *ptr++ = 2; /* assume some corect value... */ + } + + + csum_len = ptr - buf; + + if (csum_len > sizeof (buf)) + error ("impossible"); /* paranoia */ + + len = ptr - (uint8_t *) th; + if (len & 0x03) error ("impossible"); /* as >>2 ... */ + + *lenp = htons (len); + th->doff = len >> 2; + + + *packet_len_p = len; + + return 0; +} + + +static void tcp_send_probe (probe *pb, int ttl) { + int sk; + int af = dest_addr.sa.sa_family; + sockaddr_any addr; + socklen_t len = sizeof (addr); + + + /* To make sure we have chosen a free unused "source port", + just create, (auto)bind and hold a socket while the port is needed. + */ + + sk = socket (af, SOCK_STREAM, 0); + if (sk < 0) error ("socket"); + + if (reuse && setsockopt (sk, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) + error ("setsockopt SO_REUSEADDR"); + + bind_socket (sk); + + if (getsockname (sk, &addr.sa, &len) < 0) + error ("getsockname"); + + /* When we reach the target host, it can send us either RST or SYN+ACK. + For RST all is OK (we and kernel just answer nothing), but + for SYN+ACK we should reply with our RST. + It is well-known "half-open technique", used by port scanners etc. + This way we do not touch remote applications at all, unlike + the ordinary connect(2) call. + As the port-holding socket neither connect() nor listen(), + it means "no such port yet" for remote ends, and kernel always + send RST in such a situation automatically (we have to do nothing). + */ + + + th->source = addr.sin.sin_port; + + th->seq = random_seq (); + + th->check = 0; + th->check = in_csum (buf, csum_len); + + + if (ttl != last_ttl) { + + set_ttl (raw_sk, ttl); + + last_ttl = ttl; + } + + + pb->send_time = get_time (); + + if (do_send (raw_sk, th, th->doff << 2, &dest_addr) < 0) { + close (sk); + pb->send_time = 0; + return; + } + + + pb->seq = th->source; + + pb->sk = sk; + + return; +} + + +static probe *tcp_check_reply (int sk, int err, sockaddr_any *from, + char *buf, size_t len) { + probe *pb; + struct tcphdr *tcp = (struct tcphdr *) buf; + uint16_t sport, dport; + + + if (len < 8) return NULL; /* too short */ + + + if (err) { + sport = tcp->source; + dport = tcp->dest; + } else { + sport = tcp->dest; + dport = tcp->source; + } + + + if (dport != dest_port) + return NULL; + + if (!equal_addr (&dest_addr, from)) + return NULL; + + pb = probe_by_seq (sport); + if (!pb) return NULL; + + + if (!err) { + + pb->final = 1; + + if (info) + pb->ext = names_by_flags (TH_FLAGS(tcp)); + } + + return pb; +} + + +static void tcp_recv_probe (int sk, int revents) { + + if (!(revents & (POLLIN | POLLERR))) + return; + + recv_reply (sk, !!(revents & POLLERR), tcp_check_reply); +} + + +static void tcp_expire_probe (probe *pb) { + + probe_done (pb); +} + + +static tr_module tcp_ops = { + .name = "tcp", + .init = tcp_init, + .send_probe = tcp_send_probe, + .recv_probe = tcp_recv_probe, + .expire_probe = tcp_expire_probe, + .options = tcp_options, +}; + +TR_MODULE (tcp_ops); diff --git a/traceroute/mod-tcpconn.c b/traceroute/mod-tcpconn.c new file mode 100644 index 0000000..d061b6d --- /dev/null +++ b/traceroute/mod-tcpconn.c @@ -0,0 +1,228 @@ +/* + Copyright (c) 2006, 2007 Dmitry Butskoy + + License: GPL v2 or any later + + See COPYING for the status of this software. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "traceroute.h" + + +static sockaddr_any dest_addr = {{ 0, }, }; + +static int icmp_sk = -1; + + +static int tcp_init (const sockaddr_any *dest, + unsigned int port_seq, size_t *packet_len_p) { + int af = dest->sa.sa_family; + + dest_addr = *dest; + dest_addr.sin.sin_port = htons (DEF_TCP_PORT); + + if (port_seq) + dest_addr.sin.sin_port = htons (port_seq); + + + /* Currently an ICMP socket is the only way + to obtain the needed info... + */ + icmp_sk = socket (af, SOCK_RAW, (af == AF_INET) ? IPPROTO_ICMP + : IPPROTO_ICMPV6); + if (icmp_sk < 0) + error_or_perm ("socket"); + + /* icmp_sk not need full tune_socket() here, just a receiving one */ + bind_socket (icmp_sk); + use_timestamp (icmp_sk); + use_recv_ttl (icmp_sk); + + add_poll (icmp_sk, POLLIN); + + return 0; +} + + +static void tcp_send_probe (probe *pb, int ttl) { + int sk; + int af = dest_addr.sa.sa_family; + sockaddr_any addr; + socklen_t length = sizeof (addr); + + + sk = socket (af, SOCK_STREAM, 0); + if (sk < 0) error ("socket"); + + tune_socket (sk); /* common stuff */ + + set_ttl (sk, ttl); + + + pb->send_time = get_time (); + + if (connect (sk, &dest_addr.sa, sizeof (dest_addr)) < 0) { + if (errno != EINPROGRESS) + error ("connect"); + } + + + if (getsockname (sk, &addr.sa, &length) < 0) + error ("getsockname"); + + pb->seq = addr.sin.sin_port; /* both ipv4/ipv6 */ + + pb->sk = sk; + + add_poll (sk, POLLERR | POLLHUP | POLLOUT); + + return; +} + + +static probe *tcp_check_reply (int sk, int err, sockaddr_any *from, + char *buf, size_t len) { + int af = dest_addr.sa.sa_family; + int type, code, info; + probe *pb; + struct tcphdr *tcp; + + + if (len < sizeof (struct icmphdr)) + return NULL; + + + if (af == AF_INET) { + struct icmp *icmp = (struct icmp *) buf; + struct iphdr *ip; + int hlen; + + type = icmp->icmp_type; + code = icmp->icmp_code; + info = icmp->icmp_void; + + if (type != ICMP_TIME_EXCEEDED && type != ICMP_DEST_UNREACH) + return NULL; + + if (len < sizeof (struct icmphdr) + sizeof (struct iphdr) + 8) + /* `8' - rfc1122: 3.2.2 */ + return NULL; + + ip = (struct iphdr *) (((char *)icmp) + sizeof(struct icmphdr)); + hlen = ip->ihl << 2; + + if (len < sizeof (struct icmphdr) + hlen + 8) + return NULL; + if (ip->protocol != IPPROTO_TCP) + return NULL; + + tcp = (struct tcphdr *) (((char *) ip) + hlen); + + } + else { /* AF_INET6 */ + struct icmp6_hdr *icmp6 = (struct icmp6_hdr *) buf; + struct ip6_hdr *ip6; + + type = icmp6->icmp6_type; + code = icmp6->icmp6_code; + info = icmp6->icmp6_mtu; + + if (type != ICMP6_TIME_EXCEEDED && + type != ICMP6_DST_UNREACH && + type != ICMP6_PACKET_TOO_BIG + ) return NULL; + + if (len < sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr) + 8) + return NULL; + + ip6 = (struct ip6_hdr *) (icmp6 + 1); + if (ip6->ip6_nxt != IPPROTO_TCP) + return NULL; + + tcp = (struct tcphdr *) (ip6 + 1); + + } + + + if (tcp->dest != dest_addr.sin.sin_port) + return NULL; + + pb = probe_by_seq (tcp->source); + if (!pb) return NULL; + + + /* here only, high level has no data to do this */ + parse_icmp_res (pb, type, code, info); + + return pb; +} + + +static void tcp_recv_probe (int sk, int revents) { + + if (sk != icmp_sk) { /* a tcp socket */ + probe *pb; + + pb = probe_by_sk (sk); + if (!pb) { + del_poll (sk); + return; + } + + + /* do connect() again and check errno, regardless of revents */ + if (connect (sk, &dest_addr.sa, sizeof (dest_addr)) < 0) { + if (errno != EISCONN && errno != ECONNREFUSED) + return; /* ICMP say more */ + } + + /* we have reached the dest host (either connected or refused) */ + + memcpy (&pb->res, &dest_addr, sizeof (pb->res)); + + pb->final = 1; + + pb->recv_time = get_time (); + + probe_done (pb); + + return; + } + + + /* ICMP stuff */ + + if (!(revents & POLLIN)) + return; + + recv_reply (icmp_sk, 0, tcp_check_reply); +} + + +static void tcp_expire_probe (probe *pb) { + + probe_done (pb); +} + + +static tr_module tcp_ops = { + .name = "tcpconn", + .init = tcp_init, + .send_probe = tcp_send_probe, + .recv_probe = tcp_recv_probe, + .expire_probe = tcp_expire_probe, +}; + +TR_MODULE (tcp_ops); diff --git a/traceroute/mod-udp.c b/traceroute/mod-udp.c new file mode 100644 index 0000000..f3bdead --- /dev/null +++ b/traceroute/mod-udp.c @@ -0,0 +1,234 @@ +/* + Copyright (c) 2006, 2007 Dmitry Butskoy + + License: GPL v2 or any later + + See COPYING for the status of this software. +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include "traceroute.h" + + +#ifndef IPPROTO_UDPLITE +#define IPPROTO_UDPLITE 136 +#endif + +#ifndef UDPLITE_SEND_CSCOV +#define UDPLITE_SEND_CSCOV 10 +#define UDPLITE_RECV_CSCOV 11 +#endif + + +static sockaddr_any dest_addr = {{ 0, }, }; +static unsigned int curr_port = 0; +static unsigned int protocol = IPPROTO_UDP; + + +static char *data = NULL; +static size_t *length_p; + +static void fill_data (size_t *packet_len_p) { + int i; + + length_p = packet_len_p; + + if (*length_p && + !(data = malloc (*length_p)) + ) error ("malloc"); + + for (i = 0; i < *length_p; i++) + data[i] = 0x40 + (i & 0x3f); + + return; +} + + +static int udp_default_init (const sockaddr_any *dest, + unsigned int port_seq, size_t *packet_len_p) { + + curr_port = port_seq ? port_seq : DEF_START_PORT; + + dest_addr = *dest; + dest_addr.sin.sin_port = htons (curr_port); + + fill_data (packet_len_p); + + return 0; +} + + +static int udp_init (const sockaddr_any *dest, + unsigned int port_seq, size_t *packet_len_p) { + + dest_addr = *dest; + + if (!port_seq) port_seq = DEF_UDP_PORT; + dest_addr.sin.sin_port = htons ((uint16_t) port_seq); + + fill_data (packet_len_p); + + return 0; +} + + +static unsigned int coverage = 0; +#define MIN_COVERAGE (sizeof (struct udphdr)) + +static void set_coverage (int sk) { + int val = MIN_COVERAGE; + + if (setsockopt (sk, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV, + &coverage, sizeof (coverage)) < 0 + ) error ("UDPLITE_SEND_CSCOV"); + + if (setsockopt (sk, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV, + &val, sizeof (val)) < 0 + ) error ("UDPLITE_RECV_CSCOV"); +} + +static CLIF_option udplite_options[] = { + { 0, "coverage", "NUM", "Set udplite send coverage to %s (default is " + _TEXT(MIN_COVERAGE) ")", + CLIF_set_uint, &coverage, 0, CLIF_ABBREV }, + CLIF_END_OPTION +}; + +static int udplite_init (const sockaddr_any *dest, + unsigned int port_seq, size_t *packet_len_p) { + + dest_addr = *dest; + + if (!port_seq) port_seq = DEF_UDP_PORT; /* XXX: Hmmm... */ + dest_addr.sin.sin_port = htons ((uint16_t) port_seq); + + protocol = IPPROTO_UDPLITE; + + if (!coverage) coverage = MIN_COVERAGE; + + fill_data (packet_len_p); + + return 0; +} + + +static void udp_send_probe (probe *pb, int ttl) { + int sk; + int af = dest_addr.sa.sa_family; + + + sk = socket (af, SOCK_DGRAM, protocol); + if (sk < 0) error ("socket"); + + tune_socket (sk); /* common stuff */ + + if (coverage) set_coverage (sk); /* udplite case */ + + set_ttl (sk, ttl); + + + if (connect (sk, &dest_addr.sa, sizeof (dest_addr)) < 0) + error ("connect"); + + use_recverr (sk); + + + pb->send_time = get_time (); + + if (do_send (sk, data, *length_p, NULL) < 0) { + close (sk); + pb->send_time = 0; + return; + } + + + pb->sk = sk; + + add_poll (sk, POLLIN | POLLERR); + + pb->seq = dest_addr.sin.sin_port; + + if (curr_port) { /* traditional udp method */ + curr_port++; + dest_addr.sin.sin_port = htons (curr_port); /* both ipv4 and ipv6 */ + } + + return; +} + + +static probe *udp_check_reply (int sk, int err, sockaddr_any *from, + char *buf, size_t len) { + probe *pb; + + pb = probe_by_sk (sk); + if (!pb) return NULL; + + if (pb->seq != from->sin.sin_port) + return NULL; + + if (!err) pb->final = 1; + + return pb; +} + + +static void udp_recv_probe (int sk, int revents) { + + if (!(revents & (POLLIN | POLLERR))) + return; + + recv_reply (sk, !!(revents & POLLERR), udp_check_reply); +} + + +static void udp_expire_probe (probe *pb) { + + probe_done (pb); +} + + +/* All three modules share the same methods except the init... */ + +static tr_module default_ops = { + .name = "default", + .init = udp_default_init, + .send_probe = udp_send_probe, + .recv_probe = udp_recv_probe, + .expire_probe = udp_expire_probe, + .header_len = sizeof (struct udphdr), +}; + +TR_MODULE (default_ops); + + +static tr_module udp_ops = { + .name = "udp", + .init = udp_init, + .send_probe = udp_send_probe, + .recv_probe = udp_recv_probe, + .expire_probe = udp_expire_probe, + .header_len = sizeof (struct udphdr), +}; + +TR_MODULE (udp_ops); + + +static tr_module udplite_ops = { + .name = "udplite", + .init = udplite_init, + .send_probe = udp_send_probe, + .recv_probe = udp_recv_probe, + .expire_probe = udp_expire_probe, + .header_len = sizeof (struct udphdr), + .options = udplite_options, +}; + +TR_MODULE (udplite_ops); diff --git a/traceroute/module.c b/traceroute/module.c new file mode 100644 index 0000000..c83b808 --- /dev/null +++ b/traceroute/module.c @@ -0,0 +1,35 @@ +/* + Copyright (c) 2006, 2007 Dmitry Butskoy + + License: GPL v2 or any later + + See COPYING for the status of this software. +*/ + +#include +#include +#include + +#include "traceroute.h" + + +static tr_module *base = NULL; + +void tr_register_module (tr_module *ops) { + + ops->next = base; + base = ops; +} + +const tr_module *tr_get_module (const char *name) { + const tr_module *ops; + + if (!name) return 0; + + for (ops = base; ops; ops = ops->next) { + if (!strcasecmp (name, ops->name)) + return ops; + } + + return NULL; +} diff --git a/traceroute/poll.c b/traceroute/poll.c new file mode 100644 index 0000000..b8ead04 --- /dev/null +++ b/traceroute/poll.c @@ -0,0 +1,88 @@ +/* + Copyright (c) 2006, 2007 Dmitry Butskoy + + License: GPL v2 or any later + + See COPYING for the status of this software. +*/ + +#include +#include +#include +#include +#include + +#include "traceroute.h" + + +static struct pollfd *pfd = NULL; +static unsigned int num_polls = 0; + + +void add_poll (int fd, int events) { + int i; + + for (i = 0; i < num_polls && pfd[i].fd > 0; i++) ; + + if (i == num_polls) { + pfd = realloc (pfd, ++num_polls * sizeof (*pfd)); + if (!pfd) error ("realloc"); + } + + pfd[i].fd = fd; + pfd[i].events = events; +} + + +void del_poll (int fd) { + int i; + + for (i = 0; i < num_polls && pfd[i].fd != fd; i++) ; + + if (i < num_polls) pfd[i].fd = -1; /* or just zero it... */ +} + + +static int cleanup_polls (void) { + int i; + + for (i = 0; i < num_polls && pfd[i].fd > 0; i++) ; + + if (i < num_polls) { /* a hole have found */ + int j; + + for (j = i + 1; j < num_polls; j++) { + if (pfd[j].fd > 0) { + pfd[i++] = pfd[j]; + pfd[j].fd = -1; + } + } + } + + return i; +} + + +void do_poll (double timeout, void (*callback) (int fd, int revents)) { + int nfds, n, i; + + nfds = cleanup_polls (); + + if (!nfds) return; + + n = poll (pfd, nfds, ceil(timeout * 1000)); + if (n < 0) { + if (errno == EINTR) return; + error ("poll"); + } + + for (i = 0; n && i < num_polls; i++) { + if (pfd[i].revents) { + callback (pfd[i].fd, pfd[i].revents); + n--; + } + } + + return; +} + diff --git a/traceroute/random.c b/traceroute/random.c new file mode 100644 index 0000000..5a8f911 --- /dev/null +++ b/traceroute/random.c @@ -0,0 +1,28 @@ +/* + Copyright (c) 2006, 2007 Dmitry Butskoy + + License: GPL v2 or any later + + See COPYING for the status of this software. +*/ + +#include +#include +#include + +#include "traceroute.h" + + +static void __init_random_seq (void) __attribute__ ((constructor)); +static void __init_random_seq (void) { + + srand (times (NULL) + getpid ()); +} + + +unsigned int random_seq (void) { + + /* To not worry about RANDOM_MAX and precision... */ + return (rand () << 16) ^ (rand () << 8) ^ rand () ^ (rand () >> 8); +} + diff --git a/traceroute/time.c b/traceroute/time.c new file mode 100644 index 0000000..16ea824 --- /dev/null +++ b/traceroute/time.c @@ -0,0 +1,27 @@ +/* + Copyright (c) 2006, 2007 Dmitry Butskoy + + License: GPL v2 or any later + + See COPYING for the status of this software. +*/ + +#include +#include +#include + +#include "traceroute.h" + + +/* Just returns current time as double, with most possible precision... */ + +double get_time (void) { + struct timeval tv; + double d; + + gettimeofday (&tv, NULL); + + d = ((double) tv.tv_usec) / 1000000. + (unsigned long) tv.tv_sec; + + return d; +} diff --git a/traceroute/traceroute.8 b/traceroute/traceroute.8 new file mode 100644 index 0000000..9c8d232 --- /dev/null +++ b/traceroute/traceroute.8 @@ -0,0 +1,596 @@ +.\" Copyright (c) 2006 Dmitry Butskoy (dmitry@butskoy.name) +.\" License: GPL v2 or any later version +.\" See COPYING for the status of this software +.TH TRACEROUTE 8 "11 October 2006" "Traceroute" "Traceroute For Linux" +.\" .UC 6 +.SH NAME +traceroute \- print the route packets trace to network host +.SH SYNOPSIS +.na +.BR traceroute " [" \-46dFITUnreAV "] [" "\-f first_ttl" "] [" "\-g gate,..." ] +.br +.ti +8 +.BR "" [ "-i device" "] [" "-m max_ttl" "] [" "-p port" "] [" "-s src_addr" ] +.br +.ti +8 +.BR "" [ "-q nqueries" "] [" "-N squeries" "] [" "-t tos" ] +.br +.ti +8 +.BR "" [ "-l flow_label" "] [" "-w waittimes" "] [" "-z sendwait" "] [" "-UL" "] [" "-D" ] +.br +.ti +8 +.BR "" [ "-P proto" "] [" "--sport=port" "] [" "-M method" "] [" "-O mod_options" ] +.br +.ti +8 +.BR "" [ "--mtu" "] [" "--back" ] +.br +.ti +8 +.BR host " [" "packet_len" "]" +.br +.BR traceroute6 +.RI " [" options ] +.ad +.SH DESCRIPTION +.I traceroute +tracks the route packets taken from an IP network on their +way to a given host. It utilizes the IP protocol's time to live (TTL) field +and attempts to elicit an ICMP TIME_EXCEEDED response from each gateway +along the path to the host. +.P +.I traceroute6 +is equivalent to +.I traceroute +.B \-6 +.PP +The only required parameter is the name or IP address of the +destination +.BR host \ . +The optional +.B packet_len\fR`gth +is the total size of the probing packet (default 60 bytes +for IPv4 and 80 for IPv6). The specified size can be ignored +in some situations or increased up to a minimal value. +.PP +This program attempts to trace the route an IP packet would follow to some +internet host by launching probe +packets with a small ttl (time to live) then listening for an +ICMP "time exceeded" reply from a gateway. We start our probes +with a ttl of one and increase by one until we get an ICMP "port +unreachable" (or TCP reset), which means we got to the "host", or hit a max (which +defaults to 30 hops). Three probes (by default) are sent at each ttl setting +and a line is printed showing the ttl, address of the gateway and +round trip time of each probe. The address can be followed by additional +information when requested. If the probe answers come from +different gateways, the address of each responding system will +be printed. If there is no response within a certain timeout, +an "*" (asterisk) is printed for that probe. +.PP +After the trip time, some additional annotation can be printed: +.BR !H , +.BR !N , +or +.B !P +(host, network or protocol unreachable), +.B !S +(source route failed), +.B !F +(fragmentation needed), +.B !X +(communication administratively prohibited), +.B !V +(host precedence violation), +.B !C +(precedence cutoff in effect), or +.B ! +(ICMP unreachable code ). +If almost all the probes result in some kind of unreachable, traceroute +will give up and exit. +.PP +We don't want the destination host to process the UDP probe packets, +so the destination port is set to an unlikely value (you can change it with the +.B \-p +flag). There is no such a problem for ICMP or TCP tracerouting (for TCP we +use half-open technique, which prevents our probes to be seen by applications +on the destination host). +.PP +In the modern network environment the traditional traceroute methods +can not be always applicable, because of widespread use of firewalls. +Such firewalls filter the "unlikely" UDP ports, or even ICMP echoes. +To solve this, some additional tracerouting methods are implemented +(including tcp), see +.B LIST OF AVAILABLE METHODS +below. Such methods try to use particular protocol +and source/destination port, in order to bypass firewalls (to be seen +by firewalls just as a start of allowed type of a network session). +.SH OPTIONS +.TP +.BI \--help +Print help info and exit. +.TP +.BR \-4 ", " \-6 +Explicitly force IPv4 or IPv6 tracerouting. By default, the program +will try to resolve the name given, and choose the appropriate +protocol automatically. If resolving a host name returns both +IPv4 and IPv6 addresses, +.I traceroute +will use IPv4. +.TP +.B \-I, \-\-icmp +Use ICMP ECHO for probes +.TP +.B \-T, \-\-tcp +Use TCP SYN for probes +.TP +.B \-d, --debug +Enable socket level debugging (when the Linux kernel supports it) +.TP +.B \-F, --dont-fragment +Do not fragment probe packets. (For IPv4 it also sets DF bit, which tells +intermediate routers not to fragment remotely as well). +.br + +.br +Varying the size of the probing packet by the +.B packet_len +command line parameter, you can manually obtain information +about the MTU of individual network hops. The +.B \--mtu +option (see below) tries to do this automatically. +.br + +.br +Note, that non-fragmented features (like +.B \-F +or +.B \--mtu\fR) +work properly since the Linux kernel 2.6.22 only. +Before that version, IPv6 was always fragmented, IPv4 could use +the once the discovered final mtu only (from the route cache), which can be +less than the actual mtu of a device. +.TP +.BI \-f " first_ttl" ", --first=" first_ttl +Specifies with what TTL to start. Defaults to 1. +.TP +.BI \-g " gateway" ", --gateway=" gateway +Tells traceroute to add an IP source routing option to the outgoing +packet that tells the network to route the packet through the +specified +.IR gateway +(most routers have disabled source routing for security reasons). +In general, several +.IR gateway\fR's +is allowed (comma separated). For IPv6, the form of +.IR num\fB,\fIaddr\fB,\fIaddr... +is allowed, where +.IR num +is a route header type (default is type 2). Note the type 0 route header +is now deprecated (rfc5095). +.TP +.BI \-i " interface" ", --interface=" interface +Specifies the interface through which +.I traceroute +should send packets. By default, the interface is selected +according to the routing table. +.TP +.BI \-m " max_ttl" ", --max-hops=" max_ttl +Specifies the maximum number of hops (max time-to-live value) +.I traceroute +will probe. The default is 30. +.TP +.BI \-N " squeries" ", --sim-queries=" squeries +Specifies the number of probe packets sent out simultaneously. +Sending several probes concurrently can speed up +.I traceroute +considerably. The default value is 16. +.br +Note that some routers and hosts can use ICMP rate throttling. In such +a situation specifying too large number can lead to loss of some responses. +.TP +.BI \-n +Do not try to map IP addresses to host names when displaying them. +.TP +.BI \-p " port" ", --port=" port +For UDP tracing, specifies the destination port base +.I traceroute +will use (the destination port number will be incremented by each probe). +.br +For ICMP tracing, specifies the initial ICMP sequence value (incremented +by each probe too). +.br +For TCP and others specifies just the (constant) destination +port to connect. +.TP +.BI \-t " tos" ", --tos=" tos +For IPv4, set the Type of Service (TOS) and Precedence value. Useful values +are 16 (low delay) and 8 (high throughput). Note that in order to use +some TOS precedence values, you have to be super user. +.br +For IPv6, set the Traffic Control value. +.TP +.BI \-l " flow_label" ", --flowlabel=" flow_label +Use specified flow_label for IPv6 packets. +.TP +.BI \-w " max\fR[\fB,\fIhere\fB,\fInear\fR]" ", --wait=" max\fR[\fB,\fIhere\fB,\fInear\fR] +Determines how long to wait for a response to a probe. +.br + +.br +There are three (in general) float values separated by a comma +(or a slash). +.IR Max +specifies the maximum time (in seconds, default 5.0) to wait, in any case. +.br + +.br +Traditional traceroute implementation always waited whole +.IR max +seconds for any probe. But if we already have some replies from the +.B same +hop, or even from some +.B next +hop, we can use the round trip time of such a reply as a hint +to determine the actual reasonable amount of time to wait. +.br + +.br +The optional +.IR here +(default 3.0) specifies a factor to multiply the round trip time of an already +received response from the +.B same +hop. The resulting value is used as a timeout for the probe, instead of +(but no more than) +.IR max\fR. +The optional +.IR near +(default 10.0) specifies a similar factor for a response from some +.B next +hop. +(The time of the first found result is used in both cases). +.br + +.br +First, we look for the +.B same +hop (of the probe which will be printed first from now). +If nothing found, then look for some +.B next +hop. If nothing found, use +.IR max\fR. +If +.IR here +and/or +.IR near +have zero values, the corresponding computation is skipped. +.br +.IR Here +and +.IR near +are always set to zero if only +.IR max +is specified (for compatibility with previous versions). +.TP +.BI \-q " nqueries" ", --queries=" nqueries +Sets the number of probe packets per hop. The default is 3. +.TP +.BI \-r +Bypass the normal routing tables and send directly to a host on +an attached network. If the host is not on a directly-attached +network, an error is returned. This option can be used to ping a +local host through an interface that has no route through it. +.TP +.BI \-s " source_addr" ", --source=" source_addr +Chooses an alternative source address. Note that you must select the +address of one of the interfaces. +By default, the address of the outgoing interface is used. +.TP +.BI \-z " sendwait" ", --sendwait=" sendwait +Minimal time interval between probes (default 0). +If the value is more than 10, then it specifies a number in milliseconds, +else it is a number of seconds (float point values allowed too). +Useful when some routers use rate-limit for ICMP messages. +.TP +.B \-e, \-\-extensions +Show ICMP extensions (rfc4884). The general form is +.I CLASS\fB/\fITYPE\fB: +followed by a hexadecimal dump. +The MPLS (rfc4950) is shown parsed, in a form: +.B MPLS:L=\fIlabel\fB,E=\fIexp_use\fB,S=\fIstack_bottom\fB,T=\fITTL +(more objects separated by +.B / +). +.TP +.B \-A, \-\-as\-path\-lookups +Perform AS path lookups in routing registries and print results +directly after the corresponding addresses. +.TP +.B \-V, \-\-version +Print the version and exit. +.br +.P +There are additional options intended for advanced usage +(such as alternate trace methods etc.): +.TP +.B \--sport\fR=\fIport +Chooses the source port to use. Implies +.B \-N\ 1\fR\ -w\ 5 . +Normally source ports (if applicable) are chosen by the system. +.TP +.B \--fwmark\fR=\fImark +Set the firewall mark for outgoing packets (since the Linux kernel 2.6.25). +.TP +.BI \-M " method" ", --module=" name +Use specified method for traceroute operations. Default traditional udp method +has name +.IR default , +icmp +.BR "" ( "-I" ) " +and tcp +.BR "" ( "-T" ) " +have names +.I icmp +and +.I tcp +respectively. +.br +Method-specific options can be passed by +.BR \-O\ . +Most methods have their simple shortcuts, +.BR "" ( "-I " means " -M icmp" , +etc). +.TP +.BI \-O " option" ", --options=" options +Specifies some method-specific option. Several options are separated by comma (or use several +.B \-O +on cmdline). +Each method may have its own specific options, or many not have them at all. +To print information about available options, use +.BR \-O\ help . +.TP +.B \-U, \-\-udp +Use UDP to particular destination port for tracerouting (instead of increasing +the port per each probe). Default port is 53 (dns). +.TP +.BI \-UL +Use UDPLITE for tracerouting (default port is 53). +.TP +.B \-D, \-\-dccp +Use DCCP Requests for probes. +.TP +.BI \-P " protocol" ", --protocol=" protocol +Use raw packet of specified protocol for tracerouting. Default protocol is +253 (rfc3692). +.TP +.BI \--mtu +Discover MTU along the path being traced. Implies +.BR \-F\ \-N\ 1 . +New +.I mtu +is printed once in a form of +.B F=\fINUM +at the first probe of a hop which requires such +.I mtu +to be reached. (Actually, the correspond "frag needed" icmp message +normally is sent by the previous hop). +.br + +.br +Note, that some routers might cache once the seen information +on a fragmentation. Thus you can receive the final mtu from a closer hop. +Try to specify an unusual +.I tos +by +.B \-t +, this can help for one attempt (then it can be cached there as well). +.br +See +.B \-F +option for more info. +.TP +.BI \--back +Print the number of backward hops when it seems different with the forward +direction. This number is guessed in assumption that remote hops send reply +packets with initial ttl set to either 64, or 128 or 255 (which seems +a common practice). It is printed as a negate value in a form of '-NUM' . +.SH LIST OF AVAILABLE METHODS +In general, a particular traceroute method may have to be chosen by +.BR \-M\ name , +but most of the methods have their simple cmdline switches +(you can see them after the method name, if present). +.SS default +The traditional, ancient method of tracerouting. Used by default. +.P +Probe packets are udp datagrams with so-called "unlikely" destination ports. +The "unlikely" port of the first probe is 33434, then for each next probe +it is incremented by one. Since the ports are expected to be unused, +the destination host normally returns "icmp unreach port" as a final response. +(Nobody knows what happens when some application listens for such ports, +though). +.P +This method is allowed for unprivileged users. +.SS icmp \ \ \ \-I +Most usual method for now, which uses icmp echo packets for probes. +.br +If you can ping(8) the destination host, icmp tracerouting is applicable +as well. +.P +This method may be allowed for unprivileged users +since the kernel 3.0 (IPv4, for IPv6 since 3.11), which supports new +.I dgram icmp +(or +.IR \fR"\fIping\fR") +sockets. To allow such sockets, sysadmin should provide +.I net/ipv4/ping_group_range +sysctl range to match any group of the user. +.br +Options: +.TP +.B raw +Use only raw sockets (the traditional way). +.br +This way is tried first by default (for compatibility reasons), +then new dgram icmp sockets as fallback. +.TP +.B dgram +Use only dgram icmp sockets. +.SS tcp \ \ \ \ \-T +Well-known modern method, intended to bypass firewalls. +.br +Uses the constant destination port (default is 80, http). +.P +If some filters are present in the network path, then most probably +any "unlikely" udp ports (as for +.I default +method) or even icmp echoes (as for +.IR icmp ) +are filtered, and whole tracerouting will just stop at such a firewall. +To bypass a network filter, we have to use only allowed protocol/port +combinations. If we trace for some, say, mailserver, then more likely +.B \-T \-p 25 +can reach it, even when +.B \-I +can not. +.P +This method uses well-known "half-open technique", which prevents +applications on the destination host from seeing our probes at all. +Normally, a tcp syn is sent. For non-listened ports we receive tcp reset, +and all is done. For active listening ports we receive tcp syn+ack, but +answer by tcp reset (instead of expected tcp ack), this way the remote tcp +session is dropped even without the application ever taking notice. +.P +There is a couple of options for +.I tcp +method: +.TP +.B syn,ack,fin,rst,psh,urg,ece,cwr +Sets specified tcp flags for probe packet, in any combination. +.TP +.B flags\fR=\fInum +Sets the flags field in the tcp header exactly to +.IR num . +.TP +.B ecn +Send syn packet with tcp flags ECE and CWR (for Explicit Congestion +Notification, rfc3168). +.TP +.B sack,timestamps,window_scaling +Use the corresponding tcp header option in the outgoing probe packet. +.TP +.B sysctl +Use current sysctl +.IR "" ( "/proc/sys/net/*" ) +setting for the tcp header options above and +.BR ecn . +Always set by default, if nothing else specified. +.TP +.B mss\fR=\fInum +Use value of +.I num +for maxseg tcp header option (when +.BR syn ). +.TP +.B info +Print tcp flags of final tcp replies when the target host is reached. +Allows to determine whether an application listens the port and +other useful things. +.P +Default options is +.BR syn,sysctl . +.SS tcpconn +An initial implementation of tcp method, simple using connect(2) call, +which does full tcp session opening. Not recommended for normal use, because +a destination application is always affected (and can be confused). +.SS udp \ \ \ \ \-U +Use udp datagram with constant destination port (default 53, dns). +.br +Intended to bypass firewall as well. +.P +Note, that unlike in +.I tcp +method, the correspond application on the destination host +.B always +receive our probes (with random data), and most can easily be confused +by them. Most cases it will not respond to our packets though, so we will never +see the final hop in the trace. (Fortunately, it seems that at least +dns servers replies with something angry). +.P +This method is allowed for unprivileged users. +.SS udplite \ \ \-UL +Use udplite datagram for probes (with constant destination port, +default 53). +.P +This method is allowed for unprivileged users. +.br +Options: +.TP +.B coverage\fR=\fInum +Set udplite send coverage to +.IR num . +.SS dccp \ \ \-D +Use DCCP Request packets for probes (rfc4340). +.P +This method uses the same "half-open technique" as used for TCP. +The default destination port is 33434. +.P +Options: +.TP +.B service\fR=\fInum +Set DCCP service code to +.IR num +(default is 1885957735). +.SS raw \ \ \ \ \-P proto +Send raw packet of protocol +.IR proto . +.br +No protocol-specific headers are used, just IP header only. +.br +Implies +.B \-N\ 1\fR\ -w\ 5 . +.br +Options: +.TP +.B protocol\fR=\fIproto +Use IP protocol +.I proto +(default 253). +.SH NOTES +.PP +To speed up work, normally several probes are sent simultaneously. +On the other hand, it creates a "storm of packages", especially +in the reply direction. Routers can throttle the rate of icmp responses, +and some of replies can be lost. To avoid this, decrease the number +of simultaneous probes, or even set it to 1 (like in initial traceroute +implementation), i.e. +.B \-N 1 +.PP +The final (target) host can drop some of the simultaneous probes, +and might even answer only the latest ones. It can lead to extra +"looks like expired" hops near the final hop. We use a smart algorithm +to auto-detect such a situation, but if it cannot help in your case, just use +.B \-N 1 +too. +.PP +For even greater stability you can slow down the program's work by +.B \-z +option, for example use +.B \-z 0.5 +for half-second pause between probes. +.PP +To avoid an extra waiting, we use adaptive algorithm for timeouts (see +.B \-w +option for more info). It can lead to premature expiry +(especially when response times differ at times) and printing "*" +instead of a time. In such a case, switch this algorithm off, by specifying +.B \-w +with the desired timeout only (for example, +.B \-w 5\fR). +.PP +If some hops report nothing for every method, the last chance to obtain +something is to use +.B ping -R +command (IPv4, and for nearest 8 hops only). +.SH SEE ALSO +.BR ping (8), +.BR ping6 (8), +.BR tcpdump (8), +.BR netstat (8) diff --git a/traceroute/traceroute.c b/traceroute/traceroute.c new file mode 100644 index 0000000..a7477fc --- /dev/null +++ b/traceroute/traceroute.c @@ -0,0 +1,1693 @@ +/* + Copyright (c) 2006, 2007 Dmitry Butskoy + + License: GPL v2 or any later + + See COPYING for the status of this software. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* XXX: Remove this when things will be defined properly in netinet/ ... */ +#include "flowlabel.h" + +#include +#include "version.h" +#include "traceroute.h" + + +#ifndef ICMP6_DST_UNREACH_BEYONDSCOPE +#ifdef ICMP6_DST_UNREACH_NOTNEIGHBOR +#define ICMP6_DST_UNREACH_BEYONDSCOPE ICMP6_DST_UNREACH_NOTNEIGHBOR +#else +#define ICMP6_DST_UNREACH_BEYONDSCOPE 2 +#endif +#endif + +#ifndef IPV6_RECVHOPLIMIT +#define IPV6_RECVHOPLIMIT IPV6_HOPLIMIT +#endif + +#ifndef IP_PMTUDISC_PROBE +#define IP_PMTUDISC_PROBE 3 +#endif + +#ifndef IPV6_PMTUDISC_PROBE +#define IPV6_PMTUDISC_PROBE 3 +#endif + +#ifndef AI_IDN +#define AI_IDN 0 +#endif + +#ifndef NI_IDN +#define NI_IDN 0 +#endif + + +#define MAX_HOPS 255 +#define MAX_PROBES 10 +#define MAX_GATEWAYS_4 8 +#define MAX_GATEWAYS_6 127 +#define DEF_HOPS 30 +#define DEF_SIM_PROBES 16 /* including several hops */ +#define DEF_NUM_PROBES 3 +#define DEF_WAIT_SECS 5.0 +#define DEF_HERE_FACTOR 3 +#define DEF_NEAR_FACTOR 10 +#ifndef DEF_WAIT_PREC +#define DEF_WAIT_PREC 0.001 /* +1 ms to avoid precision issues */ +#endif +#define DEF_SEND_SECS 0 +#define DEF_DATA_LEN 40 /* all but IP header... */ +#define MAX_PACKET_LEN 65000 +#ifndef DEF_AF +#define DEF_AF AF_INET +#endif + +#define ttl2hops(X) (((X) <= 64 ? 65 : ((X) <= 128 ? 129 : 256)) - (X)) + + +static char version_string[] = "Modern traceroute for Linux, " + "version " _TEXT(VERSION) + "\nCopyright (c) 2016 Dmitry Butskoy, " + " License: GPL v2 or any later"; +static int debug = 0; +static unsigned int first_hop = 1; +static unsigned int max_hops = DEF_HOPS; +static unsigned int sim_probes = DEF_SIM_PROBES; +static unsigned int probes_per_hop = DEF_NUM_PROBES; + +static char **gateways = NULL; +static int num_gateways = 0; +static unsigned char *rtbuf = NULL; +static size_t rtbuf_len = 0; +static unsigned int ipv6_rthdr_type = 2; /* IPV6_RTHDR_TYPE_2 */ + +static size_t header_len = 0; +static size_t data_len = 0; + +static int dontfrag = 0; +static int noresolve = 0; +static int extension = 0; +static int as_lookups = 0; +static unsigned int dst_port_seq = 0; +static unsigned int tos = 0; +static unsigned int flow_label = 0; +static int noroute = 0; +static unsigned int fwmark = 0; +static int packet_len = -1; +static double wait_secs = DEF_WAIT_SECS; +static double here_factor = DEF_HERE_FACTOR; +static double near_factor = DEF_NEAR_FACTOR; +static double send_secs = DEF_SEND_SECS; +static int mtudisc = 0; +static int backward = 0; + +static sockaddr_any dst_addr = {{ 0, }, }; +static char *dst_name = NULL; +static char *device = NULL; +static sockaddr_any src_addr = {{ 0, }, }; +static unsigned int src_port = 0; + +static const char *module = "default"; +static const tr_module *ops = NULL; + +static char *opts[16] = { NULL, }; /* assume enough */ +static unsigned int opts_idx = 1; /* first one reserved... */ + + +static int af = 0; + +static probe *probes = NULL; +static unsigned int num_probes = 0; + + +static void ex_error (const char *format, ...) { + va_list ap; + + va_start (ap, format); + vfprintf (stderr, format, ap); + va_end (ap); + + fprintf (stderr, "\n"); + + exit (2); +} + +void error (const char *str) { + + fprintf (stderr, "\n"); + + perror (str); + + exit (1); +} + +void error_or_perm (const char *str) { + + if (errno == EPERM) + fprintf (stderr, "You do not have enough privileges to use " + "this traceroute method."); + error (str); +} + + +/* Set initial parameters according to how we was called */ + +static void check_progname (const char *name) { + const char *p; + int l; + + p = strrchr (name, '/'); + if (p) p++; + else p = name; + + l = strlen (p); + if (l <= 0) return; + l--; + + if (p[l] == '6') af = AF_INET6; + else if (p[l] == '4') af = AF_INET; + + if (!strncmp (p, "tcp", 3)) + module = "tcp"; + if (!strncmp (p, "tracert", 7)) + module = "icmp"; + + return; +} + + +static int getaddr (const char *name, sockaddr_any *addr) { + int ret; + struct addrinfo hints, *ai, *res = NULL; + + memset (&hints, 0, sizeof (hints)); + hints.ai_family = af; + hints.ai_flags = AI_IDN; + + ret = getaddrinfo (name, NULL, &hints, &res); + if (ret) { + fprintf (stderr, "%s: %s\n", name, gai_strerror (ret)); + return -1; + } + + for (ai = res; ai; ai = ai->ai_next) { + if (ai->ai_family == af) break; + /* when af not specified, choose DEF_AF if present */ + if (!af && ai->ai_family == DEF_AF) + break; + } + if (!ai) ai = res; /* anything... */ + + if (ai->ai_addrlen > sizeof (*addr)) + return -1; /* paranoia */ + memcpy (addr, ai->ai_addr, ai->ai_addrlen); + + freeaddrinfo (res); + + /* No v4mapped addresses in real network, interpret it as ipv4 anyway */ + if (addr->sa.sa_family == AF_INET6 && + IN6_IS_ADDR_V4MAPPED (&addr->sin6.sin6_addr) + ) { + if (af == AF_INET6) return -1; + addr->sa.sa_family = AF_INET; + addr->sin.sin_addr.s_addr = addr->sin6.sin6_addr.s6_addr32[3]; + } + + return 0; +} + + +static void make_fd_used (int fd) { + int nfd; + + if (fcntl (fd, F_GETFL) != -1) + return; + + if (errno != EBADF) + error ("fcntl F_GETFL"); + + nfd = open ("/dev/null", O_RDONLY); + if (nfd < 0) error ("open /dev/null"); + + if (nfd != fd) { + dup2 (nfd, fd); + close (nfd); + } + + return; +} + + +static char addr2str_buf[INET6_ADDRSTRLEN]; + +static const char *addr2str (const sockaddr_any *addr) { + + getnameinfo (&addr->sa, sizeof (*addr), + addr2str_buf, sizeof (addr2str_buf), 0, 0, NI_NUMERICHOST); + + return addr2str_buf; +} + + +/* IP options stuff */ + +static void init_ip_options (void) { + sockaddr_any *gates; + int i, max; + + if (!num_gateways) + return; + + /* check for TYPE,ADDR,ADDR... form */ + if (af == AF_INET6 && num_gateways > 1 && gateways[0]) { + char *q; + unsigned int value = strtoul (gateways[0], &q, 0); + + if (!*q) { + ipv6_rthdr_type = value; + num_gateways--; + for (i = 0; i < num_gateways; i++) + gateways[i] = gateways[i + 1]; + } + } + + + max = af == AF_INET ? MAX_GATEWAYS_4 : MAX_GATEWAYS_6; + if (num_gateways > max) + ex_error ("Too many gateways specified. No more than %d", max); + + + gates = alloca (num_gateways * sizeof (*gates)); + + for (i = 0; i < num_gateways; i++) { + + if (!gateways[i]) error ("strdup"); + + if (getaddr (gateways[i], &gates[i]) < 0) + ex_error (""); /* already reported */ + if (gates[i].sa.sa_family != af) + ex_error ("IP versions mismatch in gateway addresses"); + + free (gateways[i]); + } + + free (gateways); + gateways = NULL; + + + if (af == AF_INET) { + struct in_addr *in; + + rtbuf_len = 4 + (num_gateways + 1) * sizeof (*in); + rtbuf = malloc (rtbuf_len); + if (!rtbuf) error ("malloc"); + + in = (struct in_addr *) &rtbuf[4]; + for (i = 0; i < num_gateways; i++) + memcpy (&in[i], &gates[i].sin.sin_addr, sizeof (*in)); + /* final hop */ + memcpy (&in[i], &dst_addr.sin.sin_addr, sizeof (*in)); + i++; + + rtbuf[0] = IPOPT_NOP; + rtbuf[1] = IPOPT_LSRR; + rtbuf[2] = (i * sizeof (*in)) + 3; + rtbuf[3] = IPOPT_MINOFF; + + } + else if (af == AF_INET6) { + struct in6_addr *in6; + struct ip6_rthdr *rth; + + /* IPV6_RTHDR_TYPE_0 length is 8 */ + rtbuf_len = 8 + num_gateways * sizeof (*in6); + rtbuf = malloc (rtbuf_len); + if (!rtbuf) error ("malloc"); + + rth = (struct ip6_rthdr *) rtbuf; + rth->ip6r_nxt = 0; + rth->ip6r_len = 2 * num_gateways; + rth->ip6r_type = ipv6_rthdr_type; + rth->ip6r_segleft = num_gateways; + + *((uint32_t *) (rth + 1)) = 0; + + in6 = (struct in6_addr *) (rtbuf + 8); + for (i = 0; i < num_gateways; i++) + memcpy (&in6[i], &gates[i].sin6.sin6_addr, sizeof (*in6)); + } + + return; +} + + +/* Command line stuff */ + +static int set_af (CLIF_option *optn, char *arg) { + int vers = (long) optn->data; + + if (vers == 4) af = AF_INET; + else if (vers == 6) af = AF_INET6; + else + return -1; + + return 0; +} + +static int add_gateway (CLIF_option *optn, char *arg) { + + if (num_gateways >= MAX_GATEWAYS_6) { /* 127 > 8 ... :) */ + fprintf (stderr, "Too many gateways specified."); + return -1; + } + + gateways = realloc (gateways, (num_gateways + 1) * sizeof (*gateways)); + if (!gateways) error ("malloc"); + gateways[num_gateways++] = strdup (arg); + + return 0; +} + +static int set_source (CLIF_option *optn, char *arg) { + + return getaddr (arg, &src_addr); +} + +static int set_port (CLIF_option *optn, char *arg) { + unsigned int *up = (unsigned int *) optn->data; + char *q; + + *up = strtoul (arg, &q, 0); + if (q == arg) { + struct servent *s = getservbyname (arg, NULL); + + if (!s) return -1; + *up = ntohs (s->s_port); + } + + return 0; +} + +static int set_module (CLIF_option *optn, char *arg) { + + module = (char *) optn->data; + + return 0; +} + + +static int set_mod_option (CLIF_option *optn, char *arg) { + + if (!strcmp (arg, "help")) { + const tr_module *mod = tr_get_module (module); + + if (mod && mod->options) { + /* just to set common keyword flag... */ + CLIF_parse (1, &arg, 0, 0, CLIF_KEYWORD); + CLIF_print_options (NULL, mod->options); + } else + fprintf (stderr, "No options for module `%s'\n", module); + + exit (0); + } + + if (opts_idx >= sizeof (opts) / sizeof (*opts)) { + fprintf (stderr, "Too many module options\n"); + return -1; + } + + opts[opts_idx] = strdup (arg); + if (!opts[opts_idx]) error ("strdup"); + opts_idx++; + + return 0; +} + + +static int set_raw (CLIF_option *optn, char *arg) { + char buf[1024]; + + module = "raw"; + + snprintf (buf, sizeof (buf), "protocol=%s", arg); + return set_mod_option (optn, buf); +} + + +static int set_wait_specs (CLIF_option *optn, char *arg) { + char *p, *q; + + here_factor = near_factor = 0; + + wait_secs = strtod (p = arg, &q); + if (q == p) return -1; + if (!*q++) return 0; + + here_factor = strtod (p = q, &q); + if (q == p) return -1; + if (!*q++) return 0; + + near_factor = strtod (p = q, &q); + if (q == p || *q) return -1; + + return 0; +} + + +static int set_host (CLIF_argument *argm, char *arg, int index) { + + if (getaddr (arg, &dst_addr) < 0) + return -1; + + dst_name = arg; + + /* i.e., guess it by the addr in cmdline... */ + if (!af) af = dst_addr.sa.sa_family; + + return 0; +} + + +static CLIF_option option_list[] = { + { "4", 0, 0, "Use IPv4", set_af, (void *) 4, 0, CLIF_EXTRA }, + { "6", 0, 0, "Use IPv6", set_af, (void *) 6, 0, 0 }, + { "d", "debug", 0, "Enable socket level debugging", + CLIF_set_flag, &debug, 0, 0 }, + { "F", "dont-fragment", 0, "Do not fragment packets", + CLIF_set_flag, &dontfrag, 0, CLIF_ABBREV }, + { "f", "first", "first_ttl", "Start from the %s hop (instead from 1)", + CLIF_set_uint, &first_hop, 0, 0 }, + { "g", "gateway", "gate", "Route packets through the specified gateway " + "(maximum " _TEXT(MAX_GATEWAYS_4) " for IPv4 and " + _TEXT(MAX_GATEWAYS_6) " for IPv6)", + add_gateway, 0, 0, CLIF_SEVERAL }, + { "I", "icmp", 0, "Use ICMP ECHO for tracerouting", + set_module, "icmp", 0, 0 }, + { "T", "tcp", 0, "Use TCP SYN for tracerouting (default " + "port is " _TEXT(DEF_TCP_PORT) ")", + set_module, "tcp", 0, 0 }, + { "i", "interface", "device", "Specify a network interface " + "to operate with", + CLIF_set_string, &device, 0, 0 }, + { "m", "max-hops", "max_ttl", "Set the max number of hops (max TTL " + "to be reached). Default is " _TEXT(DEF_HOPS) , + CLIF_set_uint, &max_hops, 0, 0 }, + { "N", "sim-queries", "squeries", "Set the number of probes " + "to be tried simultaneously (default is " + _TEXT(DEF_SIM_PROBES) ")", + CLIF_set_uint, &sim_probes, 0, 0 }, + { "n", 0, 0, "Do not resolve IP addresses to their domain names", + CLIF_set_flag, &noresolve, 0, 0 }, + { "p", "port", "port", "Set the destination port to use. " + "It is either initial udp port value for " + "\"default\" method (incremented by each probe, " + "default is " _TEXT(DEF_START_PORT) "), " + "or initial seq for \"icmp\" (incremented as well, " + "default from 1), or some constant destination port" + " for other methods (with default of " + _TEXT(DEF_TCP_PORT) " for \"tcp\", " + _TEXT(DEF_UDP_PORT) " for \"udp\", etc.)", + set_port, &dst_port_seq, 0, 0 }, + { "t", "tos", "tos", "Set the TOS (IPv4 type of service) or TC " + "(IPv6 traffic class) value for outgoing packets", + CLIF_set_uint, &tos, 0, 0 }, + { "l", "flowlabel", "flow_label", "Use specified %s for IPv6 packets", + CLIF_set_uint, &flow_label, 0, 0 }, + { "w", "wait", "MAX,HERE,NEAR", "Wait for a probe no more than HERE " + "(default " _TEXT(DEF_HERE_FACTOR) ") times longer " + "than a response from the same hop, or no more " + "than NEAR (default " _TEXT(DEF_NEAR_FACTOR) ") " + "times than some next hop, or MAX (default " + _TEXT(DEF_WAIT_SECS) ") seconds " + "(float point values allowed too)", + set_wait_specs, 0, 0, 0 }, + { "q", "queries", "nqueries", "Set the number of probes per each hop. " + "Default is " _TEXT(DEF_NUM_PROBES), + CLIF_set_uint, &probes_per_hop, 0, 0 }, + { "r", 0, 0, "Bypass the normal routing and send directly to a host " + "on an attached network", + CLIF_set_flag, &noroute, 0, 0 }, + { "s", "source", "src_addr", "Use source %s for outgoing packets", + set_source, 0, 0, 0 }, + { "z", "sendwait", "sendwait", "Minimal time interval between probes " + "(default " _TEXT(DEF_SEND_SECS) "). If the value " + "is more than 10, then it specifies a number " + "in milliseconds, else it is a number of seconds " + "(float point values allowed too)", + CLIF_set_double, &send_secs, 0, 0 }, + { "e", "extensions", 0, "Show ICMP extensions (if present), " + "including MPLS", + CLIF_set_flag, &extension, 0, CLIF_ABBREV }, + { "A", "as-path-lookups", 0, "Perform AS path lookups in routing " + "registries and print results directly after " + "the corresponding addresses", + CLIF_set_flag, &as_lookups, 0, 0 }, + { "M", "module", "name", "Use specified module (either builtin or " + "external) for traceroute operations. Most methods " + "have their shortcuts (`-I' means `-M icmp' etc.)", + CLIF_set_string, &module, 0, CLIF_EXTRA }, + { "O", "options", "OPTS", "Use module-specific option %s for the " + "traceroute module. Several %s allowed, separated " + "by comma. If %s is \"help\", print info about " + "available options", + set_mod_option, 0, 0, CLIF_SEVERAL | CLIF_EXTRA }, + { 0, "sport", "num", "Use source port %s for outgoing packets. " + "Implies `-N 1'", + set_port, &src_port, 0, CLIF_EXTRA }, +#ifdef SO_MARK + { 0, "fwmark", "num", "Set firewall mark for outgoing packets", + CLIF_set_uint, &fwmark, 0, 0 }, +#endif + { "U", "udp", 0, "Use UDP to particular port for tracerouting " + "(instead of increasing the port per each probe), " + "default port is " _TEXT(DEF_UDP_PORT), + set_module, "udp", 0, CLIF_EXTRA }, + { 0, "UL", 0, "Use UDPLITE for tracerouting (default dest port is " + _TEXT(DEF_UDP_PORT) ")", + set_module, "udplite", 0, CLIF_ONEDASH|CLIF_EXTRA }, + { "D", "dccp", 0, "Use DCCP Request for tracerouting (default " + "port is " _TEXT(DEF_DCCP_PORT) ")", + set_module, "dccp", 0, CLIF_EXTRA }, + { "P", "protocol", "prot", "Use raw packet of protocol %s " + "for tracerouting", + set_raw, 0, 0, CLIF_EXTRA }, + { 0, "mtu", 0, "Discover MTU along the path being traced. " + "Implies `-F -N 1'", + CLIF_set_flag, &mtudisc, 0, CLIF_EXTRA }, + { 0, "back", 0, "Guess the number of hops in the backward path " + "and print if it differs", + CLIF_set_flag, &backward, 0, CLIF_EXTRA }, + CLIF_VERSION_OPTION (version_string), + CLIF_HELP_OPTION, + CLIF_END_OPTION +}; + +static CLIF_argument arg_list[] = { + { "host", "The host to traceroute to", + set_host, 0, CLIF_STRICT }, + { "packetlen", "The full packet length (default is the length of " + "an IP header plus " _TEXT(DEF_DATA_LEN) "). Can be " + "ignored or increased to a minimal allowed value", + CLIF_arg_int, &packet_len, 0 }, + CLIF_END_ARGUMENT +}; + + +static void do_it (void); + +int main (int argc, char *argv[]) { + + setlocale (LC_ALL, ""); + setlocale (LC_NUMERIC, "C"); /* avoid commas in msec printed */ + + check_progname (argv[0]); + + + if (CLIF_parse (argc, argv, option_list, arg_list, + CLIF_MAY_JOIN_ARG | CLIF_HELP_EMPTY) < 0 + ) exit (2); + + + ops = tr_get_module (module); + if (!ops) ex_error ("Unknown traceroute module %s", module); + + + if (!first_hop || first_hop > max_hops) + ex_error ("first hop out of range"); + if (max_hops > MAX_HOPS) + ex_error ("max hops cannot be more than " _TEXT(MAX_HOPS)); + if (!probes_per_hop || probes_per_hop > MAX_PROBES) + ex_error ("no more than " _TEXT(MAX_PROBES) " probes per hop"); + if (wait_secs < 0 || here_factor < 0 || near_factor < 0) + ex_error ("bad wait specifications `%g,%g,%g' used", + wait_secs, here_factor, near_factor); + if (packet_len > MAX_PACKET_LEN) + ex_error ("too big packetlen %d specified", packet_len); + if (src_addr.sa.sa_family && src_addr.sa.sa_family != af) + ex_error ("IP version mismatch in addresses specified"); + if (send_secs < 0) + ex_error ("bad sendtime `%g' specified", send_secs); + if (send_secs >= 10) /* it is milliseconds */ + send_secs /= 1000; + + if (af == AF_INET6 && (tos || flow_label)) + dst_addr.sin6.sin6_flowinfo = + htonl (((tos & 0xff) << 20) | (flow_label & 0x000fffff)); + + if (src_port) { + src_addr.sin.sin_port = htons ((uint16_t) src_port); + src_addr.sa.sa_family = af; + } + + if (src_port || ops->one_per_time) { + sim_probes = 1; + here_factor = near_factor = 0; + } + + + /* make sure we don't std{in,out,err} to open sockets */ + make_fd_used (0); + make_fd_used (1); + make_fd_used (2); + + + init_ip_options (); + + header_len = (af == AF_INET ? sizeof (struct iphdr) + : sizeof (struct ip6_hdr)) + + rtbuf_len + ops->header_len; + + if (mtudisc) { + dontfrag = 1; + sim_probes = 1; + if (packet_len < 0) + packet_len = MAX_PACKET_LEN; + } + + if (packet_len < 0) { + if (DEF_DATA_LEN >= ops->header_len) + data_len = DEF_DATA_LEN - ops->header_len; + } else { + if (packet_len >= header_len) + data_len = packet_len - header_len; + } + + + num_probes = max_hops * probes_per_hop; + probes = calloc (num_probes, sizeof (*probes)); + if (!probes) error ("calloc"); + + + if (ops->options && opts_idx > 1) { + opts[0] = strdup (module); /* aka argv[0] ... */ + if (CLIF_parse (opts_idx, opts, ops->options, 0, CLIF_KEYWORD) < 0) + exit (2); + } + + if (ops->init (&dst_addr, dst_port_seq, &data_len) < 0) + ex_error ("trace method's init failed"); + + + do_it (); + + return 0; +} + + +/* PRINT STUFF */ + +static void print_header (void) { + + /* Note, without ending new-line! */ + printf ("traceroute to %s (%s), %u hops max, %zu byte packets", + dst_name, addr2str (&dst_addr), max_hops, + header_len + data_len); + fflush (stdout); +} + + +static void print_addr (sockaddr_any *res) { + const char *str; + + if (!res->sa.sa_family) + return; + + str = addr2str (res); + + + if (noresolve) + printf (" %s", str); + else { + char buf[1024]; + + buf[0] = '\0'; + getnameinfo (&res->sa, sizeof (*res), buf, sizeof (buf), + 0, 0, NI_IDN); + printf (" %s (%s)", buf[0] ? buf : str, str); + } + + if (as_lookups) + printf (" [%s]", get_as_path (str)); +} + + +static void print_probe (probe *pb) { + unsigned int idx = (pb - probes); + unsigned int ttl = idx / probes_per_hop + 1; + unsigned int np = idx % probes_per_hop; + + if (np == 0) + printf ("\n%2u ", ttl); + + + if (!pb->res.sa.sa_family) + printf (" *"); + else { + int prn = !np; /* print if the first... */ + + if (np) { /* ...and if differs with previous */ + probe *p; + + /* skip expired */ + for (p = pb - 1; np && !p->res.sa.sa_family; p--, np--) ; + + if (!np || + !equal_addr (&p->res, &pb->res) || + (p->ext != pb->ext && + !(p->ext && pb->ext && !strcmp (p->ext, pb->ext))) || + (backward && p->recv_ttl != pb->recv_ttl) + ) prn = 1; + } + + if (prn) { + print_addr (&pb->res); + + if (pb->ext) printf (" <%s>", pb->ext); + + if (backward && pb->recv_ttl) { + int hops = ttl2hops (pb->recv_ttl); + if (hops != ttl) printf (" '-%d'", hops); + } + } + } + + + if (pb->recv_time) { + double diff = pb->recv_time - pb->send_time; + + printf (" %.3f ms", diff * 1000); + } + + if (pb->err_str[0]) + printf (" %s", pb->err_str); + + + fflush (stdout); + + return; +} + + +static void print_end (void) { + + printf ("\n"); +} + + +/* Compute timeout stuff */ + +static double get_timeout (probe *pb) { + double value; + + if (here_factor) { + /* check for already replied from the same hop */ + int i, idx = (pb - probes); + probe *p = &probes[idx - (idx % probes_per_hop)]; + + for (i = 0; i < probes_per_hop; i++, p++) { + /* `p == pb' skipped since !pb->done */ + + if (p->done && (value = p->recv_time - p->send_time) > 0) { + value += DEF_WAIT_PREC; + value *= here_factor; + return value < wait_secs ? value : wait_secs; + } + } + } + + if (near_factor) { + /* check forward for already replied */ + probe *p, *endp = probes + num_probes; + + for (p = pb + 1; p < endp && p->send_time; p++) { + + if (p->done && (value = p->recv_time - p->send_time) > 0) { + value += DEF_WAIT_PREC; + value *= near_factor; + return value < wait_secs ? value : wait_secs; + } + } + } + + return wait_secs; +} + + +/* Check expiration stuff */ + +static void check_expired (probe *pb) { + int idx = (pb - probes); + probe *p, *endp = probes + num_probes; + probe *fp = NULL, *pfp = NULL; + + if (!pb->done) /* an ops method still not release it */ + return; + + + /* check all the previous in the same hop */ + for (p = &probes[idx - (idx % probes_per_hop)]; p < pb; p++) { + + if (!p->done || /* too early to decide something */ + !p->final /* already ttl-exceeded in the same hop */ + ) return; + + pfp = p; /* some of the previous probes is final */ + } + + /* check forward all the sent probes */ + for (p = pb + 1; p < endp && p->send_time; p++) { + + if (p->done) { /* some next probe already done... */ + if (!p->final) /* ...was ttl-exceeded. OK, we are expired. */ + return; + else { + fp = p; + break; + } + } + } + + if (!fp) /* no any final probe found. Assume expired. */ + return; + + + /* Well. There is a situation "*(this) * * * * ... * * final" + We cannot guarantee that "final" is in its right place. + We've sent "sim_probes" simultaneously, and the final hop + can drop some of them and answer only for latest ones. + If we can detect/assume that it so, then just put "final" + to the (pseudo-expired) "this" place. + */ + + /* It seems that the case of "answers for latest ones only" + occurs mostly with icmp_unreach error answers ("!H" etc.). + Icmp_echoreply, tcp_reset and even icmp_port_unreach looks + like going in the right order. + */ + if (!fp->err_str[0]) /* not an icmp_unreach error report... */ + return; + + + if (pfp || + (idx % probes_per_hop) + (fp - pb) < probes_per_hop + ) { + /* Either some previous (pfp) or some next probe + in this hop is final. It means that the whole hop is final. + Do the replace (it also causes further "final"s to be shifted + here too). + */ + goto replace_by_final; + } + + + /* If the final probe is an icmp_unreachable report + (either in a case of some error, like "!H", or just port_unreach), + it could follow the "time-exceed" report from the *same* hop. + */ + for (p = pb - 1; p >= probes; p--) { + if (equal_addr (&p->res, &fp->res)) { + /* ...Yes. Put "final" to the "this" place. */ + goto replace_by_final; + } + } + + + if (fp->recv_ttl) { + /* Consider the ttl value of the report packet and guess where + the "final" should be. If it seems that it should be + in the same hop as "this", then do replace. + */ + int back_hops, ttl; + + /* We assume that the reporting one has an initial ttl value + of either 64, or 128, or 255. It is most widely used + in the modern routers and computers. + The idea comes from tracepath(1) routine. + */ + back_hops = ttl2hops (fp->recv_ttl); + + /* It is possible that the back path differs from the forward + and therefore has different number of hops. To minimize + such an influence, get the nearest previous time-exceeded + probe and compare with it. + */ + for (p = pb - 1; p >= probes; p--) { + if (p->done && !p->final && p->recv_ttl) { + int hops = ttl2hops (p->recv_ttl); + + if (hops < back_hops) { + ttl = (p - probes) / probes_per_hop + 1; + back_hops = (back_hops - hops) + ttl; + break; + } + } + } + + ttl = idx / probes_per_hop + 1; + if (back_hops == ttl) + /* Yes! It seems that "final" should be at "this" place */ + goto replace_by_final; + else if (back_hops < ttl) + /* Hmmm... Assume better to replace here too... */ + goto replace_by_final; + + } + + + /* No idea what to do. Assume expired. */ + + return; + + +replace_by_final: + + *pb = *fp; + + memset (fp, 0, sizeof (*fp)); + /* block extra re-send */ + fp->send_time = 1.; + + return; +} + + +probe *probe_by_seq (int seq) { + int n; + + if (seq <= 0) return NULL; + + for (n = 0; n < num_probes; n++) { + if (probes[n].seq == seq) + return &probes[n]; + } + + return NULL; +} + +probe *probe_by_sk (int sk) { + int n; + + if (sk <= 0) return NULL; + + for (n = 0; n < num_probes; n++) { + if (probes[n].sk == sk) + return &probes[n]; + } + + return NULL; +} + + +static void poll_callback (int fd, int revents) { + + ops->recv_probe (fd, revents); +} + + +static void do_it (void) { + int start = (first_hop - 1) * probes_per_hop; + int end = num_probes; + double last_send = 0; + + print_header (); + + + while (start < end) { + int n, num = 0; + double next_time = 0; + double now_time = get_time (); + + + for (n = start; n < end; n++) { + probe *pb = &probes[n]; + + + if (n == start && /* probably time to print... */ + !pb->done && pb->send_time /* ...but yet not replied */ + ) { + double expire_time = pb->send_time + get_timeout (pb); + + if (expire_time > now_time) + next_time = expire_time; + else { + ops->expire_probe (pb); + check_expired (pb); + } + } + + + if (pb->done) { + + if (n == start) { /* can print it now */ + print_probe (pb); + start++; + } + + if (pb->final) + end = (n / probes_per_hop + 1) * probes_per_hop; + + continue; + } + + + if (!pb->send_time) { + int ttl; + double next; + + if (send_secs && (next = last_send + send_secs) > now_time) { + next_time = next; + break; + } + + ttl = n / probes_per_hop + 1; + + ops->send_probe (pb, ttl); + + if (!pb->send_time) { + if (next_time) break; /* have chances later */ + else error ("send probe"); + } + + last_send = pb->send_time; + } + + + if (!next_time) + next_time = pb->send_time + get_timeout (pb); + + num++; + if (num >= sim_probes) break; + } + + + if (next_time) { + double timeout = next_time - get_time (); + + if (timeout < 0) timeout = 0; + + do_poll (timeout, poll_callback); + } + + } + + + print_end (); + + return; +} + + +void tune_socket (int sk) { + int i = 0; + + if (debug) { + i = 1; + if (setsockopt (sk, SOL_SOCKET, SO_DEBUG, &i, sizeof (i)) < 0) + error ("setsockopt SO_DEBUG"); + } + + +#ifdef SO_MARK + if (fwmark) { + if (setsockopt (sk, SOL_SOCKET, SO_MARK, + &fwmark, sizeof (fwmark)) < 0 + ) error ("setsockopt SO_MARK"); + } +#endif + + + if (rtbuf && rtbuf_len) { + if (af == AF_INET) { + if (setsockopt (sk, IPPROTO_IP, IP_OPTIONS, + rtbuf, rtbuf_len) < 0 + ) error ("setsockopt IP_OPTIONS"); + } + else if (af == AF_INET6) { + if (setsockopt (sk, IPPROTO_IPV6, IPV6_RTHDR, + rtbuf, rtbuf_len) < 0 + ) error ("setsockopt IPV6_RTHDR"); + } + } + + + bind_socket (sk); + + + if (af == AF_INET) { + + i = dontfrag ? IP_PMTUDISC_PROBE : IP_PMTUDISC_DONT; + if (setsockopt (sk, SOL_IP, IP_MTU_DISCOVER, &i, sizeof(i)) < 0 && + (!dontfrag || (i = IP_PMTUDISC_DO, + setsockopt (sk, SOL_IP, IP_MTU_DISCOVER, &i, sizeof(i)) < 0)) + ) error ("setsockopt IP_MTU_DISCOVER"); + + if (tos) { + i = tos; + if (setsockopt (sk, SOL_IP, IP_TOS, &i, sizeof (i)) < 0) + error ("setsockopt IP_TOS"); + } + + } + else if (af == AF_INET6) { + + i = dontfrag ? IPV6_PMTUDISC_PROBE : IPV6_PMTUDISC_DONT; + if (setsockopt (sk, SOL_IPV6, IPV6_MTU_DISCOVER,&i,sizeof(i)) < 0 && + (!dontfrag || (i = IPV6_PMTUDISC_DO, + setsockopt (sk, SOL_IPV6, IPV6_MTU_DISCOVER,&i,sizeof(i)) < 0)) + ) error ("setsockopt IPV6_MTU_DISCOVER"); + + + if (flow_label) { + struct in6_flowlabel_req flr; + + memset (&flr, 0, sizeof (flr)); + flr.flr_label = htonl (flow_label & 0x000fffff); + flr.flr_action = IPV6_FL_A_GET; + flr.flr_flags = IPV6_FL_F_CREATE; + flr.flr_share = IPV6_FL_S_ANY; + memcpy (&flr.flr_dst, &dst_addr.sin6.sin6_addr, + sizeof (flr.flr_dst)); + + if (setsockopt (sk, IPPROTO_IPV6, IPV6_FLOWLABEL_MGR, + &flr, sizeof (flr)) < 0 + ) error ("setsockopt IPV6_FLOWLABEL_MGR"); + } + + if (tos) { + i = tos; + if (setsockopt (sk, IPPROTO_IPV6, IPV6_TCLASS, + &i, sizeof (i)) < 0 + ) error ("setsockopt IPV6_TCLASS"); + } + + if (tos || flow_label) { + i = 1; + if (setsockopt (sk, IPPROTO_IPV6, IPV6_FLOWINFO_SEND, + &i, sizeof (i)) < 0 + ) error ("setsockopt IPV6_FLOWINFO_SEND"); + } + } + + + if (noroute) { + i = noroute; + if (setsockopt (sk, SOL_SOCKET, SO_DONTROUTE, &i, sizeof (i)) < 0) + error ("setsockopt SO_DONTROUTE"); + } + + + use_timestamp (sk); + + use_recv_ttl (sk); + + fcntl (sk, F_SETFL, O_NONBLOCK); + + return; +} + + +void parse_icmp_res (probe *pb, int type, int code, int info) { + char *str = NULL; + char buf[sizeof (pb->err_str)]; + + if (af == AF_INET) { + + if (type == ICMP_TIME_EXCEEDED) { + if (code == ICMP_EXC_TTL) + return; + } + else if (type == ICMP_DEST_UNREACH) { + + switch (code) { + case ICMP_UNREACH_NET: + case ICMP_UNREACH_NET_UNKNOWN: + case ICMP_UNREACH_ISOLATED: + case ICMP_UNREACH_TOSNET: + str = "!N"; + break; + + case ICMP_UNREACH_HOST: + case ICMP_UNREACH_HOST_UNKNOWN: + case ICMP_UNREACH_TOSHOST: + str = "!H"; + break; + + case ICMP_UNREACH_NET_PROHIB: + case ICMP_UNREACH_HOST_PROHIB: + case ICMP_UNREACH_FILTER_PROHIB: + str = "!X"; + break; + + case ICMP_UNREACH_PORT: + /* dest host is reached */ + str = ""; + break; + + case ICMP_UNREACH_PROTOCOL: + str = "!P"; + break; + + case ICMP_UNREACH_NEEDFRAG: + snprintf (buf, sizeof (buf), "!F-%d", info); + str = buf; + break; + + case ICMP_UNREACH_SRCFAIL: + str = "!S"; + break; + + case ICMP_UNREACH_HOST_PRECEDENCE: + str = "!V"; + break; + + case ICMP_UNREACH_PRECEDENCE_CUTOFF: + str = "!C"; + break; + + default: + snprintf (buf, sizeof (buf), "!<%u>", code); + str = buf; + break; + } + } + + } + else if (af == AF_INET6) { + + if (type == ICMP6_TIME_EXCEEDED) { + if (code == ICMP6_TIME_EXCEED_TRANSIT) + return; + } + else if (type == ICMP6_DST_UNREACH) { + + switch (code) { + + case ICMP6_DST_UNREACH_NOROUTE: + str = "!N"; + break; + + case ICMP6_DST_UNREACH_BEYONDSCOPE: + case ICMP6_DST_UNREACH_ADDR: + str = "!H"; + break; + + case ICMP6_DST_UNREACH_ADMIN: + str = "!X"; + break; + + case ICMP6_DST_UNREACH_NOPORT: + /* dest host is reached */ + str = ""; + break; + + default: + snprintf (buf, sizeof (buf), "!<%u>", code); + str = buf; + break; + } + } + else if (type == ICMP6_PACKET_TOO_BIG) { + snprintf (buf, sizeof (buf), "!F-%d", info); + str = buf; + } + } + + + if (!str) { + snprintf (buf, sizeof (buf), "!<%u-%u>", type, code); + str = buf; + } + + if (*str) { + strncpy (pb->err_str, str, sizeof (pb->err_str)); + pb->err_str[sizeof (pb->err_str) - 1] = '\0'; + } + + pb->final = 1; + + return; +} + + +static void parse_local_res (probe *pb, int ee_errno, int info) { + + if (ee_errno == EMSGSIZE && info != 0) { + snprintf (pb->err_str, sizeof(pb->err_str)-1, "!F-%d", info); + pb->final = 1; + return; + } + + errno = ee_errno; + error ("local recverr"); +} + + +void probe_done (probe *pb) { + + if (pb->sk) { + del_poll (pb->sk); + close (pb->sk); + pb->sk = 0; + } + + pb->seq = 0; + + pb->done = 1; +} + + +void recv_reply (int sk, int err, check_reply_t check_reply) { + struct msghdr msg; + sockaddr_any from; + struct iovec iov; + int n; + probe *pb; + char buf[1280]; /* min mtu for ipv6 ( >= 576 for ipv4) */ + char *bufp = buf; + char control[1024]; + struct cmsghdr *cm; + double recv_time = 0; + int recv_ttl = 0; + struct sock_extended_err *ee = NULL; + + + memset (&msg, 0, sizeof (msg)); + msg.msg_name = &from; + msg.msg_namelen = sizeof (from); + msg.msg_control = control; + msg.msg_controllen = sizeof (control); + iov.iov_base = buf; + iov.iov_len = sizeof (buf); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + + n = recvmsg (sk, &msg, err ? MSG_ERRQUEUE : 0); + if (n < 0) return; + + + /* when not MSG_ERRQUEUE, AF_INET returns full ipv4 header + on raw sockets... + */ + + if (!err && + af == AF_INET && + /* XXX: Assume that the presence of an extra header means + that it is not a raw socket... + */ + ops->header_len == 0 + ) { + struct iphdr *ip = (struct iphdr *) bufp; + int hlen; + + if (n < sizeof (struct iphdr)) return; + + hlen = ip->ihl << 2; + if (n < hlen) return; + + bufp += hlen; + n -= hlen; + } + + + pb = check_reply (sk, err, &from, bufp, n); + if (!pb) { + + /* for `frag needed' case at the local host, + kernel >= 3.13 sends local error (no more icmp) + */ + if (!n && err && dontfrag) { + pb = &probes[(first_hop - 1) * probes_per_hop]; + if (pb->done) return; + } else + return; + } + + + /* Parse CMSG stuff */ + + for (cm = CMSG_FIRSTHDR (&msg); cm; cm = CMSG_NXTHDR (&msg, cm)) { + void *ptr = CMSG_DATA (cm); + + if (cm->cmsg_level == SOL_SOCKET) { + + if (cm->cmsg_type == SO_TIMESTAMP) { + struct timeval *tv = (struct timeval *) ptr; + + recv_time = tv->tv_sec + tv->tv_usec / 1000000.; + } + } + else if (cm->cmsg_level == SOL_IP) { + + if (cm->cmsg_type == IP_TTL) + recv_ttl = *((int *) ptr); + else if (cm->cmsg_type == IP_RECVERR) { + + ee = (struct sock_extended_err *) ptr; + + if (ee->ee_origin != SO_EE_ORIGIN_ICMP && + ee->ee_origin != SO_EE_ORIGIN_LOCAL + ) return; + + /* dgram icmp sockets might return extra things... */ + if (ee->ee_origin == SO_EE_ORIGIN_ICMP && + (ee->ee_type == ICMP_SOURCE_QUENCH || + ee->ee_type == ICMP_REDIRECT) + ) return; + } + } + else if (cm->cmsg_level == SOL_IPV6) { + + if (cm->cmsg_type == IPV6_HOPLIMIT) + recv_ttl = *((int *) ptr); + else if (cm->cmsg_type == IPV6_RECVERR) { + + ee = (struct sock_extended_err *) ptr; + + if (ee->ee_origin != SO_EE_ORIGIN_ICMP6 && + ee->ee_origin != SO_EE_ORIGIN_LOCAL + ) return; + } + } + } + + if (!recv_time) + recv_time = get_time (); + + + if (!err) + memcpy (&pb->res, &from, sizeof (pb->res)); + + pb->recv_time = recv_time; + + pb->recv_ttl = recv_ttl; + + if (ee && ee->ee_origin != SO_EE_ORIGIN_LOCAL) { /* icmp or icmp6 */ + memcpy (&pb->res, SO_EE_OFFENDER (ee), sizeof(pb->res)); + parse_icmp_res (pb, ee->ee_type, ee->ee_code, ee->ee_info); + } + + if (ee && ee->ee_origin == SO_EE_ORIGIN_LOCAL) + parse_local_res (pb, ee->ee_errno, ee->ee_info); + + + if (ee && + mtudisc && + ee->ee_info >= header_len && + ee->ee_info < header_len + data_len + ) { + data_len = ee->ee_info - header_len; + + probe_done (pb); + + /* clear this probe (as actually the previous hop answers here) + but fill its `err_str' by the info obtained. Ugly, but easy... + */ + memset (pb, 0, sizeof (*pb)); + snprintf (pb->err_str, sizeof(pb->err_str)-1, "F=%d", ee->ee_info); + + return; + } + + + if (ee && + extension && + header_len + n >= (128 + 8) && /* at least... (rfc4884) */ + header_len <= 128 && /* paranoia */ + ((af == AF_INET && (ee->ee_type == ICMP_TIME_EXCEEDED || + ee->ee_type == ICMP_DEST_UNREACH || + ee->ee_type == ICMP_PARAMETERPROB)) || + (af == AF_INET6 && (ee->ee_type == ICMP6_TIME_EXCEEDED || + ee->ee_type == ICMP6_DST_UNREACH)) + ) + ) { + int step; + int offs = 128 - header_len; + + if (n > data_len) step = 0; /* guaranteed at 128 ... */ + else + step = af == AF_INET ? 4 : 8; + + handle_extensions (pb, bufp + offs, n - offs, step); + } + + + probe_done (pb); +} + + +int equal_addr (const sockaddr_any *a, const sockaddr_any *b) { + + if (!a->sa.sa_family) + return 0; + + if (a->sa.sa_family != b->sa.sa_family) + return 0; + + if (a->sa.sa_family == AF_INET6) + return !memcmp (&a->sin6.sin6_addr, &b->sin6.sin6_addr, + sizeof (a->sin6.sin6_addr)); + else + return !memcmp (&a->sin.sin_addr, &b->sin.sin_addr, + sizeof (a->sin.sin_addr)); + return 0; /* not reached */ +} + + +void bind_socket (int sk) { + sockaddr_any *addr, tmp; + + if (device) { + if (setsockopt (sk, SOL_SOCKET, SO_BINDTODEVICE, + device, strlen (device) + 1) < 0 + ) error ("setsockopt SO_BINDTODEVICE"); + } + + if (!src_addr.sa.sa_family) { + memset (&tmp, 0, sizeof (tmp)); + tmp.sa.sa_family = af; + addr = &tmp; + } else + addr = &src_addr; + + if (bind (sk, &addr->sa, sizeof (*addr)) < 0) + error ("bind"); + + return; +} + + +void use_timestamp (int sk) { + int n = 1; + + setsockopt (sk, SOL_SOCKET, SO_TIMESTAMP, &n, sizeof (n)); + /* foo on errors... */ +} + + +void use_recv_ttl (int sk) { + int n = 1; + + if (af == AF_INET) + setsockopt (sk, SOL_IP, IP_RECVTTL, &n, sizeof (n)); + else if (af == AF_INET6) + setsockopt (sk, SOL_IPV6, IPV6_RECVHOPLIMIT, &n, sizeof (n)); + /* foo on errors */ +} + + +void use_recverr (int sk) { + int val = 1; + + if (af == AF_INET) { + if (setsockopt (sk, SOL_IP, IP_RECVERR, &val, sizeof (val)) < 0) + error ("setsockopt IP_RECVERR"); + } + else if (af == AF_INET6) { + if (setsockopt (sk, SOL_IPV6, IPV6_RECVERR, &val, sizeof (val)) < 0) + error ("setsockopt IPV6_RECVERR"); + } +} + + +void set_ttl (int sk, int ttl) { + + if (af == AF_INET) { + if (setsockopt (sk, SOL_IP, IP_TTL, &ttl, sizeof (ttl)) < 0) + error ("setsockopt IP_TTL"); + } + else if (af == AF_INET6) { + if (setsockopt (sk, SOL_IPV6, IPV6_UNICAST_HOPS, + &ttl, sizeof (ttl)) < 0 + ) error ("setsockopt IPV6_UNICAST_HOPS"); + } +} + + +int do_send (int sk, const void *data, size_t len, const sockaddr_any *addr) { + int res; + + if (!addr || raw_can_connect ()) + res = send (sk, data, len, 0); + else + res = sendto (sk, data, len, 0, &addr->sa, sizeof (*addr)); + + if (res < 0) { + if (errno == ENOBUFS || errno == EAGAIN) + return res; + if (errno == EMSGSIZE || errno == EHOSTUNREACH) + return 0; /* recverr will say more... */ + error ("send"); /* not recoverable */ + } + + return res; +} + + +/* There is a bug in the kernel before 2.6.25, which prevents icmp errors + to be obtained by MSG_ERRQUEUE for ipv6 connected raw sockets. +*/ +static int can_connect = -1; + +#define VER(A,B,C,D) (((((((A) << 8) | (B)) << 8) | (C)) << 8) | (D)) + +int raw_can_connect (void) { + + if (can_connect < 0) { + + if (af == AF_INET) + can_connect = 1; + else { /* AF_INET6 */ + struct utsname uts; + int n; + unsigned int a, b, c, d = 0; + + if (uname (&uts) < 0) + return 0; + + n = sscanf (uts.release, "%u.%u.%u.%u", &a, &b, &c, &d); + can_connect = (n >= 3 && VER (a, b, c, d) >= VER (2, 6, 25, 0)); + } + } + + return can_connect; +} diff --git a/traceroute/traceroute.h b/traceroute/traceroute.h new file mode 100644 index 0000000..53dc793 --- /dev/null +++ b/traceroute/traceroute.h @@ -0,0 +1,107 @@ +/* + Copyright (c) 2006, 2007 Dmitry Butskoy + + License: GPL v2 or any later + + See COPYING for the status of this software. +*/ + +#include + +#include + + +union common_sockaddr { + struct sockaddr sa; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; +}; +typedef union common_sockaddr sockaddr_any; + +struct probe_struct { + int done; + int final; + sockaddr_any res; + double send_time; + double recv_time; + int recv_ttl; + int sk; + int seq; + char *ext; + char err_str[16]; /* assume enough */ +}; +typedef struct probe_struct probe; + + +struct tr_module_struct { + struct tr_module_struct *next; + const char *name; + int (*init) (const sockaddr_any *dest, + unsigned int port_seq, size_t *packet_len); + void (*send_probe) (probe *pb, int ttl); + void (*recv_probe) (int fd, int revents); + void (*expire_probe) (probe *pb); + CLIF_option *options; /* per module options, if any */ + int one_per_time; /* no simultaneous probes */ + size_t header_len; /* additional header length (aka for udp) */ +}; +typedef struct tr_module_struct tr_module; + + +#define __TEXT(X) #X +#define _TEXT(X) __TEXT(X) + +#define DEF_START_PORT 33434 /* start for traditional udp method */ +#define DEF_UDP_PORT 53 /* dns */ +#define DEF_TCP_PORT 80 /* web */ +#define DEF_DCCP_PORT DEF_START_PORT /* is it a good choice?... */ +#define DEF_RAW_PROT 253 /* for experimentation and testing, rfc3692 */ + + +void error (const char *str) __attribute__((noreturn)); +void error_or_perm (const char *str) __attribute__((noreturn)); + +double get_time (void); +void tune_socket (int sk); +void parse_icmp_res (probe *pb, int type, int code, int info); +void probe_done (probe *pb); + +typedef probe *(*check_reply_t) (int sk, int err, sockaddr_any *from, + char *buf, size_t len); +void recv_reply (int sk, int err, check_reply_t check_reply); + +int equal_addr (const sockaddr_any *a, const sockaddr_any *b); + +probe *probe_by_seq (int seq); +probe *probe_by_sk (int sk); + +void bind_socket (int sk); +void use_timestamp (int sk); +void use_recv_ttl (int sk); +void use_recverr (int sk); +void set_ttl (int sk, int ttl); +int do_send (int sk, const void *data, size_t len, const sockaddr_any *addr); + +void add_poll (int fd, int events); +void del_poll (int fd); +void do_poll (double timeout, void (*callback) (int fd, int revents)); + +void handle_extensions (probe *pb, char *buf, int len, int step); +const char *get_as_path (const char *query); + +int raw_can_connect (void); + +unsigned int random_seq (void); +uint16_t in_csum (const void *ptr, size_t len); + + +void tr_register_module (tr_module *module); +const tr_module *tr_get_module (const char *name); + +#define TR_MODULE(MOD) \ +static void __init_ ## MOD (void) __attribute__ ((constructor)); \ +static void __init_ ## MOD (void) { \ + \ + tr_register_module (&MOD); \ +} + diff --git a/wrappers/Makefile b/wrappers/Makefile new file mode 100644 index 0000000..08dbbc6 --- /dev/null +++ b/wrappers/Makefile @@ -0,0 +1,15 @@ +# +# Copyright (c) 2007 Dmitry Butskoy +# +# License: GPL v2 or any later +# +# See COPYING for the status of this software. +# + +# +# Nothing to do. +# + +all depend install: + @true + diff --git a/wrappers/README.wrappers b/wrappers/README.wrappers new file mode 100644 index 0000000..bd0e2f4 --- /dev/null +++ b/wrappers/README.wrappers @@ -0,0 +1,18 @@ + +This directory contains various wrappers for traceroute, which provide +some compatibility for some another traceroute-like software, +including tcptraceroute(8). + +These wrappers try to emulate the command-line interface only. +The actual output is the output of the underlain traceroute(8). + +Some of options and features certainly are not supported +(especially if it is considered too extra :) ). + +The idea of such wrappers is mostly not wrappers itself. It is to inspect +the features implemented by competitors, whether it is already supported by us, +or it is useful to be supported, or it is not useful or excessive. + +We have no plans to implement all features of all existing software. +Let the people a chance to play with something... ;) + diff --git a/wrappers/lft b/wrappers/lft new file mode 100755 index 0000000..81dbc70 --- /dev/null +++ b/wrappers/lft @@ -0,0 +1,117 @@ +#!/bin/sh +# +# Copyright (c) 2007 Dmitry K. Butskoy +# +# License: GPL v2 or any later +# +# See COPYING for the status of this software. +# + +# +# Shell wrapper providing lft(8) command line interface. +# +# The original implementation of lft(8) can be obtained +# from http://pwhois.org/lft/ +# + + +prgname=$0 +opts="" +length="" +method="-T" + +dport="" +sport="" +queries=2 +ahead=5 +scatter=20 +timeout=1000 + + +usage () { + echo "Usage: $prgname [-ACEFINRSTUVbehinpruvz] [-d dport] [-s sport] + [-m retry min] [-M retry max] [-a ahead] [-c scatter ms] [-t timeout ms] + [-l min ttl] [-H max ttl] [-L length] [-q ISN] [-D device] [--help] + [gateway ...] target:dport" >&2 +} + +warning () { + echo "$prgname: Option '$1' is not implemented in this wrapper" >&2 +} + + +PARSED=`getopt -o 'ACEFINRSTUVbehinpruvzd:s:m:M:a:c:t:l:H:L:q:D:' -l help -- "$@"` +[ $? != 0 ] && exit 2 + +eval set -- "$PARSED" + +while [ $# -gt 0 ] +do + case "$1" in + -d) dport=$2; shift 2 ;; + -s) sport=$2; shift 2 ;; + -z) sport=""; shift ;; + -m) queries=$2; shift 2 ;; + -a) ahead=$2; shift 2 ;; + -c) scatter=$2; shift 2 ;; + -t) timeout=$2; shift 2 ;; + -l) opts="$opts -f $2"; shift 2 ;; + -L) length=$2; shift 2 ;; + -D) case "$2" in + [0-9]*) opts="$opts -s $2" ;; + *) opts="$opts -i $2" ;; + esac + shift 2 + ;; + -H) opts="$opts -m $2"; shift 2 ;; + -I) opts="$opts -t 0x10"; shift ;; + -n) opts="$opts -n"; shift ;; + -h) shift ;; + -F) opts="$opts -O fin"; shift ;; + -u) method="-U"; shift ;; + -N) opts="$opts -A"; shift ;; + -p) method="-I"; shift ;; + -b) shift ;; + -A) opts="$opts -A"; shift ;; + -[ieErCTUV]) warning $1; shift ;; + -[Mq]) warning $1; shift 2 ;; + -[RS]) shift ;; + --help) usage; exit 0 ;; + -v) echo "\"lft\"-compatible wrapper for new Linux Traceroute" >&2; + exit 0 ;; + --) shift; break ;; + *) echo "$prgname: Internal parsing error" >&2; exit 2 ;; + esac +done + +while [ $# -gt 1 ] +do + opts="$opts -g $1" + shift +done + +[ $# -eq 0 ] && { + usage + exit 2 +} + +case "$1" in + *:*:*) host=$1 ;; + *:*) dport=${1##*:}; host=${1%:*} ;; + *) host=$1 ;; +esac + +[ -n "$dport" ] && opts="$opts -p $dport" +[ -n "$sport" ] && opts="$opts --sport=$sport" +opts="$opts -q $queries" +opts="$opts -N $(($ahead * $queries))" +opts="$opts -z $scatter" + +timeout=`printf "%04d" $timeout` +sec=${timeout%???} +opts="$opts -w $sec.${timeout#$sec}" + +opts="$method $opts" + +exec traceroute $opts $host $length + diff --git a/wrappers/tcptraceroute b/wrappers/tcptraceroute new file mode 100755 index 0000000..b4fc810 --- /dev/null +++ b/wrappers/tcptraceroute @@ -0,0 +1,78 @@ +#!/bin/sh +# +# Copyright (c) 2007 Dmitry Butskoy +# +# License: GPL v2 or any later +# +# See COPYING for the status of this software. +# + +# +# Shell wrapper providing tcptraceroute(8) command line interface. +# +# The original implementation of tcptraceroute(8) can be obtained +# from http://michael.toren.net/code/tcptraceroute/ +# + +opts="-T -O info" +length="" +prgname=$0 + + +usage () { + echo "Usage: $prgname [-hvnFSAE] [-i dev] [-f furst_ttl] [-l length] + [-q nqueries] [-t tos] [-m max_ttl] [-p src_port] [-s src_addr] + [-w wait_time] host [dest_port] [length]" >&2 +} + + +PARSED=`getopt 'hvdnNi:l:f:Fm:p:q:w:s:t:SAE' "$@"` +[ $? != 0 ] && exit 2 + +eval set -- "$PARSED" + +while [ $# -gt 0 ] +do + case "$1" in + -[dnF]) opts="$opts $1"; shift ;; + -N) shift ;; + -[ifmqwst]) opts="$opts $1 $2"; shift 2 ;; + -l) length=$2; shift 2 ;; + -p) opts="$opts --sport=$2"; shift 2 ;; + -S) opts="$opts -O syn"; shift ;; + -A) opts="$opts -O ack"; shift ;; + -E) opts="$opts -O ecn"; shift ;; + -h) usage ; exit 0 ;; + -v) echo "\"tcptraceroute\"-compatible wrapper for new Linux Traceroute" >&2; + exit 0 ;; + --) shift; break ;; + *) echo "$prgname: Internal parsing error" >&2; exit 2 ;; + esac +done + +[ $# -eq 0 ] && { + usage + exit 2 +} + +host=$1 +shift + +[ $# -gt 0 ] && { + opts="$opts -p $1" + shift +} + +[ $# -gt 0 ] && { + length=$1 + shift +} + + +# Say to the people it is actually another program... +echo "Running: + traceroute $opts $host $length" >&2 + + +exec traceroute $opts $host $length + diff --git a/wrappers/tcptraceroute.8 b/wrappers/tcptraceroute.8 new file mode 100644 index 0000000..7c19ecf --- /dev/null +++ b/wrappers/tcptraceroute.8 @@ -0,0 +1,34 @@ +.\" Copyright (c) 2016 Dmitry Butskoy (dmitry@butskoy.name) +.\" License: GPL v2 or any later version +.\" See COPYING for the status of this software +.TH TCPTRACEROUTE 8 "8 March 2016" "Traceroute" "Traceroute For Linux" +.\" .UC 6 +.SH NAME +tcptraceroute \- print the route packets trace to network host +.SH SYNOPSIS +.na +.BR tcptraceroute +.RI " [" options ] +.ad +.SH DESCRIPTION +.I tcptraceroute +is just a link to the system +.B traceroute\fR, +to allow run it without specifying +.B \-T +option each time (for switch to the TCP method). It is fully equivalent to +.B traceroute \-T\fR, +the rest of the command line is the same. +.br +See +.BR traceroute (8) +for more info. +.SH NOTES +There was an original implementation of tcptraceroute, which had some options +differ (but rare used). Most common +.BR "" [ "dnFi:f:m:q:w:s:t:" ] +was exactly the same, but +.BR "" [ "l:p:NSAE" ] +was not. For full compatibility, a wrapper provided (available in the source code). +.SH SEE ALSO +.BR traceroute (8) diff --git a/wrappers/tracepath b/wrappers/tracepath new file mode 100755 index 0000000..987b998 --- /dev/null +++ b/wrappers/tracepath @@ -0,0 +1,63 @@ +#!/bin/sh +# +# Copyright (c) 2008 Dmitry Butskoy +# +# License: GPL v2 or any later +# +# See COPYING for the status of this software. +# + +# +# Shell wrapper providing tracepath(1) or tracepath6(1) command line interface. +# +# The original implementation of tracepath/tracepath6 can be obtained +# from the iputils package, available at http://www.skbuff.net/iputils/ +# + +opts="-q1 --mtu --back" +port=44444 +prgname=$0 +length="" + + +usage () { + echo "Usage: $prgname [-nbh] [ -l pktlen ] host[/port]" >&2 +} + + +PARSED=`getopt 'hnbl:' "$@"` +[ $? != 0 ] && exit 2 + +eval set -- "$PARSED" + +while [ $# -gt 0 ] +do + case "$1" in + -n) opts="$opts $1"; shift ;; + -b) shift ;; + -l) length=$2; shift 2 ;; + -h) usage ; exit 0 ;; + --) shift; break ;; + *) echo "$prgname: Internal parsing error" >&2; exit 2 ;; + esac +done + +[ $# -eq 0 ] && { + usage + exit 2 +} + +host=$1 +case "$host" in + */*) port=${host##*/} + host=${host%/*} + ;; +esac + + +case "$prgname" in + *6) opts="$opts -6" ;; +esac + +exec traceroute $opts -p $port $host $length + diff --git a/wrappers/traceproto b/wrappers/traceproto new file mode 100755 index 0000000..988fdc5 --- /dev/null +++ b/wrappers/traceproto @@ -0,0 +1,127 @@ +#!/bin/sh +# +# Copyright (c) 2007 Dmitry K. Butskoy +# +# License: GPL v2 or any later +# +# See COPYING for the status of this software. +# + +# +# Shell wrapper providing traceproto(8) command line interface. +# +# The original implementation of traceproto(8) can be obtained +# from http://traceproto.sourceforge.net/ +# + + +prgname=$0 +opts="" +length="" +method="tcp" +sendwait=100 +cont="" +iface=$TP_DEFAULT_IF + + +usage () { + echo "Usage: $prgname [-cCTfAhvR] [-p protocol] [-d dst_port] [-D max_dst_port] + [-s src_port] [-S max_src_port] [-m min_ttl] [-M max_ttl] [-w response_timeout] + [-W send_delay] [-a account_level] [-P payload_size] + [-F interface] [-k skips] [-I consecutive_trace_count] + [-H packets_per_hop] [-i incr_pattern] [-o output_style] [-t tcp_flags] + target" >&2 +} + +warning () { + echo "$prgname: Option '$1' is not implemented in this wrapper" >&2 +} + + +PARSED=`getopt 'cCTfAhvRp:d:D:s:S:m:M:w:W:a:P:F:k:I:H:i:o:t:' "$@"` +[ $? != 0 ] && exit 2 + +eval set -- "$PARSED" + +while [ $# -gt 0 ] +do + case "$1" in + -p) method=$2; shift 2 ;; + -d) opts="$opts -p $2"; shift 2 ;; + -s) opts="$opts --sport=$2"; shift 2 ;; + -m) opts="$opts -f $2"; shift 2 ;; + -M) opts="$opts -m $2"; shift 2 ;; + -w) opts="$opts -w $2"; shift 2 ;; + -W) sendwait=$2; shift 2 ;; + -P) length=$2; shift 2 ;; + -c) cont=100000; shift ;; + -I) cont=$2; shift 2 ;; + -H) opts="$opts -q $2"; shift 2 ;; + -f) opts="$opts -F"; shift ;; + -F) iface=$2; shift 2 ;; + -A) opts="$opts -A"; shift ;; + -o) [ $2 != "c" ] && warning $1; shift 2 ;; + -t) case $2 in + *S*) opts="$opts -O syn" ;; + esac + case $2 in + *A*) opts="$opts -O ack" ;; + esac + case $2 in + *R*) opts="$opts -O rst" ;; + esac + case $2 in + *U*) opts="$opts -O urg" ;; + esac + case $2 in + *P*) opts="$opts -O psh" ;; + esac + case $2 in + *F*) opts="$opts -O fin" ;; + esac + case $2 in + *E*) opts="$opts -O ece" ;; + esac + case $2 in + *C*) opts="$opts -O cwr" ;; + esac + shift 2 ;; + -[DSaki]) warning $1; shift 2 ;; + -[TCR]) warning $1; shift ;; + -h) usage; exit 0 ;; + -v) echo "\"traceproto\"-compatible wrapper for new Linux Traceroute" >&2; + exit 0 ;; + --) shift; break ;; + *) echo "$prgname: Internal parsing error" >&2; exit 2 ;; + esac +done + + + +[ $# -eq 0 ] && { + usage + exit 2 +} + +host=$1 + + +opts="-M $method $opts" +opts="$opts -z $sendwait" +[ -n "$iface" ] && opts="$opts -i $iface" + +[ -n "$TP_OUTPUT_STYLE" -a "$TP_OUTPUT_STYLE" != "classic" ] && { + echo "$prgname: warning: only classic output style supported" >&2 +} + +[ -n "$TP_RA_SERVER" -a -z "$RA_SERVER" ] && RA_SERVER=$TP_RA_SERVER + + +[ -z "$cont" ] && exec traceroute $opts $host $length + +while [ "$cont" -gt 0 ] +do + cont=$(($cont - 1)) + traceroute $opts $host $length +done + diff --git a/wrappers/traceroute-nanog b/wrappers/traceroute-nanog new file mode 100755 index 0000000..29fd2a4 --- /dev/null +++ b/wrappers/traceroute-nanog @@ -0,0 +1,76 @@ +#!/bin/sh +# +# Copyright (c) 2007 Dmitry K. Butskoy +# +# License: GPL v2 or any later +# +# See COPYING for the status of this software. +# + +# +# Shell wrapper providing traceroute-nanog(8) command line interface. +# +# The original implementation of traceroute-nanog(8) can be obtained +# from ftp://ftp.login.com/pub/software/traceroute/ +# + + +prgname=$0 +opts="" +spray="" + + +usage () { + echo "Usage: $prgname [-adnruvAMOPQU$] [-w wait] [-S start_ttl] + [-m max_ttl] [-p port] [-q nqueries] [-g gateway] [-t tos] + [-s src_addr] [-I proto] host [data_size]" >&2 +} + +warning () { + echo "$prgname: Option '$1' is not implemented in this wrapper" >&2 +} + + +PARSED=`getopt 'adnruvAMOPQU$w:S:m:p:q:g:t:s:I:f:T:' "$@"` +[ $? != 0 ] && { + usage + exit 2 +} + +eval set -- "$PARSED" + +while [ $# -gt 0 ] +do + case "$1" in + -[Adrn]) opts="$opts $1"; shift ;; + -[gmpqstw]) opts="$opts $1 $2"; shift 2 ;; + -I) case "$2" in + icmp) opts="$opts -I" ;; + tcp) opts="$opts -T" ;; + udp) ;; + udplite) opts="$opts -UL" ;; + *) opts="$opts -P $2" ;; + esac + shift 2 ;; + -S) opts="$opts -f $2"; shift 2 ;; + -P) spray=1; shift ;; + -f) opts="$opts --sport=$2"; shift 2 ;; + -u) ;; + -$) opts="-f 64 -m 64 -q 1"; shift ;; + -M) opts="$opts --mtu"; shift ;; + -[aUOvQ]) warning $1; shift ;; + -T) warning $1; shift 2 ;; + --) shift; break ;; + *) echo "$prgname: Internal parsing error" >&2; exit 2 ;; + esac +done + +[ $# -eq 0 ] && { + usage + exit 2 +} + +[ -z "$spray" ] && opts="$opts -N 1" + +exec traceroute $opts $1 $2 + -- cgit v1.2.3