traceroute-2.0.20/0000755000014400001440000000000012347116023012433 5ustar bucuserstraceroute-2.0.20/chvers.sh0000555000014400001440000000447312250646636014305 0ustar bucusers#!/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 traceroute-2.0.20/COPYING0000644000014400001440000004325412242662376013511 0ustar bucusers 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. traceroute-2.0.20/wrappers/0000755000014400001440000000000012203736367014310 5ustar bucuserstraceroute-2.0.20/wrappers/tracepath0000755000014400001440000000213112203736367016206 0ustar bucusers#!/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 traceroute-2.0.20/wrappers/README.wrappers0000644000014400001440000000134612201446364017027 0ustar bucusers 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... ;) traceroute-2.0.20/wrappers/Makefile0000644000014400001440000000037712205407441015745 0ustar bucusers# # 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 traceroute-2.0.20/wrappers/traceroute-nanog0000755000014400001440000000310612213374076017507 0ustar bucusers#!/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 traceroute-2.0.20/wrappers/tcptraceroute0000755000014400001440000000270012203705420017103 0ustar bucusers#!/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 } exec traceroute $opts $host $length traceroute-2.0.20/wrappers/traceproto0000755000014400001440000000547112135014752016416 0ustar bucusers#!/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 traceroute-2.0.20/wrappers/lft0000755000014400001440000000466112170244011015011 0ustar bucusers#!/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 traceroute-2.0.20/README0000644000014400001440000000253012242704555013322 0ustar bucusersThis 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 * 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 - 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 traceroute-2.0.20/VERSION0000644000014400001440000000002712347075456013520 0ustar bucusers#define VERSION 2.0.20 traceroute-2.0.20/Makefile0000644000014400001440000000403612205410727014077 0ustar bucusers# # 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) traceroute-2.0.20/traceroute.spec0000644000014400001440000000276212347075456015511 0ustar bucusersSummary: Traces the route taken by packets over an IPv4/IPv6 network Name: traceroute Version: 2.0.20 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 traceroute-2.0.20/TODO0000644000014400001440000000377112242704612013134 0ustar bucusers* 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... :) * Tune default parameters properly. Maybe decrease `-w' secs to 4.0 instead of 5.0 (as Network is faster now). * Verbose output. Show method in use, what ports we are connecting to, more info about icmp response etc. * 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... * Make the output more clean -- use two spaces before any printed address (not the first one only), and use two spaces between expired probes as well. * Maybe implement some config file (both user and system-wide), with directives (auto)generated from the general-type long-options. This could allow people to tune default parameters. The presence of such files even makes it reasonable to add some more long-options (fe. to tune the form of output), as the user can specify it in configfile just at once. traceroute-2.0.20/libsupp/0000755000014400001440000000000012347116050014111 5ustar bucuserstraceroute-2.0.20/libsupp/clif.h0000644000014400001440000001070212200212101015155 0ustar bucusers/* 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 */ traceroute-2.0.20/libsupp/clif.c0000644000014400001440000006535712347077207015224 0ustar bucusers/* 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) ? -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) ? -1 : 0; } static int set_double (double *data, char *arg) { char *q; if (!data) return -1; *data = strtod (arg, &q); return (q == arg) ? -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); } traceroute-2.0.20/CREDITS0000644000014400001440000000125112347116022013451 0ustar bucusersThanks for the testings and proposals: 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) ... maybe you too? ;) traceroute-2.0.20/traceroute/0000755000014400001440000000000012347116050014610 5ustar bucuserstraceroute-2.0.20/traceroute/module.c0000644000014400001440000000111212172250653016240 0ustar bucusers/* 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; } traceroute-2.0.20/traceroute/mod-tcpconn.c0000644000014400001440000001074012253070524017200 0ustar bucusers/* 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); traceroute-2.0.20/traceroute/mod-raw.c0000644000014400001440000000544512253070326016333 0ustar bucusers/* 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); traceroute-2.0.20/traceroute/poll.c0000644000014400001440000000266212172250667015741 0ustar bucusers/* 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 "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, 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; } traceroute-2.0.20/traceroute/random.c0000644000014400001440000000107012172250675016242 0ustar bucusers/* 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); } traceroute-2.0.20/traceroute/extension.c0000644000014400001440000000514312170532664017002 0ustar bucusers#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 u_int8_t reserved1; u_int16_t checksum; } __attribute__ ((packed)); struct icmp_ext_object { u_int16_t length; u_int8_t class; u_int8_t c_type; u_int8_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) != (u_int16_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; u_int32_t *ui = (u_int32_t *) obj->data; int i, n; if (objlen < sizeof (*obj) || objlen > len ) return -1; data_len = objlen - sizeof (*obj); if (data_len % sizeof (u_int32_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++) { u_int32_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; } traceroute-2.0.20/traceroute/flowlabel.h0000644000014400001440000000157312223301262016730 0ustar bucusers/* 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 traceroute-2.0.20/traceroute/as_lookups.c0000644000014400001440000000502712347075201017141 0ustar bucusers/* 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 "!!"; } traceroute-2.0.20/traceroute/mod-udp.c0000644000014400001440000001067612253070200016323 0ustar bucusers/* 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 ((u_int16_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 ((u_int16_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); traceroute-2.0.20/traceroute/csum.c0000644000014400001440000000113012172250633015720 0ustar bucusers/* 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" u_int16_t in_csum (const void *ptr, size_t len) { const u_int16_t *p = (const u_int16_t *) ptr; size_t nw = len / 2; unsigned int sum = 0; u_int16_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; } traceroute-2.0.20/traceroute/traceroute.h0000644000014400001440000000544112242674663017157 0ustar bucusers/* 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); u_int16_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); \ } traceroute-2.0.20/traceroute/mod-tcp.c0000644000014400001440000002576712124575653016353 0ustar bucusers/* 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 u_int8_t buf[1024]; /* enough, enough... */ static size_t csum_len = 0; static struct tcphdr *th = NULL; #define TH_FLAGS(TH) (((u_int8_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 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 option for tcp", set_flag, (void *) FL_SACK, 0, 0 }, { 0, "timestamps", 0, "Use timestamps option for tcp", set_flag, (void *) FL_TSTAMP, 0, CLIF_ABBREV }, { 0, "window_scaling", 0, "Use 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, "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]; u_int8_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; u_int8_t *ptr; u_int16_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 = (u_int16_t *) ptr; ptr += sizeof (u_int16_t); *((u_int16_t *) ptr) = htons ((u_int16_t) IPPROTO_TCP); ptr += sizeof (u_int16_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 = (u_int8_t *) (th + 1); if (flags & TH_SYN) { *ptr++ = TCPOPT_MAXSEG; /* 2 */ *ptr++ = TCPOLEN_MAXSEG; /* 4 */ *((u_int16_t *) ptr) = htons (mss ? mss : mtu); ptr += sizeof (u_int16_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 */ *((u_int32_t *) ptr) = random_seq (); /* really! */ ptr += sizeof (u_int32_t); *((u_int32_t *) ptr) = (flags & TH_ACK) ? random_seq () : 0; ptr += sizeof (u_int32_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 - (u_int8_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"); 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; u_int16_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); traceroute-2.0.20/traceroute/mod-dccp.c0000644000014400001440000001470012242704431016444 0ustar bucusers/* 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 u_int8_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; u_int8_t *ptr; u_int16_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 = (u_int16_t *) ptr; ptr += sizeof (u_int16_t); *((u_int16_t *) ptr) = htons ((u_int16_t) IPPROTO_DCCP); ptr += sizeof (u_int16_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 - (u_int8_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; u_int16_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, .one_per_time = 0, .options = dccp_options, }; TR_MODULE (dccp_ops); traceroute-2.0.20/traceroute/mod-icmp.c0000644000014400001440000001150112204162100016444 0ustar bucusers/* 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 u_int16_t seq = 1; static u_int16_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; u_int16_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); traceroute-2.0.20/traceroute/traceroute.c0000644000014400001440000010753712347115442017152 0ustar bucusers/* 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 #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_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) ", " __DATE__ "\nCopyright (c) 2008 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 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); 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; *((u_int32_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_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", "waittime", "Set the number of seconds to wait for " "response to a probe (default is " _TEXT(DEF_WAIT_SECS) "). Non-integer (float point) " "values allowed too", CLIF_set_double, &wait_secs, 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) ex_error ("bad wait seconds `%g' specified", wait_secs); 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 ((u_int16_t) src_port); src_addr.sa.sa_family = af; } if (src_port || ops->one_per_time) sim_probes = 1; /* 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"); } /* 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 max_time = 0; double now_time = get_time (); for (n = start; n < end; n++) { probe *pb = &probes[n]; if (!pb->done && pb->send_time && now_time - pb->send_time >= wait_secs ) { 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; if (send_secs && (now_time - last_send) < send_secs) { max_time = (last_send + send_secs) - wait_secs; break; } ttl = n / probes_per_hop + 1; ops->send_probe (pb, ttl); if (!pb->send_time) { if (max_time) break; /* have chances later */ else error ("send probe"); } last_send = pb->send_time; } if (pb->send_time > max_time) max_time = pb->send_time; num++; if (num >= sim_probes) break; } if (max_time) { double timeout = (max_time + wait_secs) - now_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; } 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) 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 = NULL; /* dgram icmp sockets might return extra things... */ else if (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 = NULL; } } } 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) { 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 && 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) return 0; /* icmp 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; } traceroute-2.0.20/traceroute/time.c0000644000014400001440000000076512172250721015722 0ustar bucusers/* 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; } traceroute-2.0.20/traceroute/traceroute.80000644000014400001440000004050412347076662017077 0ustar bucusers.\" 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 waittime" "] [" "-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 5.0 seconds (default), 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 " waittime" ", --wait=" waittime Set the time (in seconds) to wait for a response to a probe (default 5.0 sec). .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 is a couple of additional options, intended for an advanced usage (another trace methods etc.): .TP .B \--sport\fR=\fIport Chooses the source port to use. Implies .BR \-N\ 1 . 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 only), 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 .BR \-N\ 1 . .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 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) traceroute-2.0.20/store.sh0000555000014400001440000000301112206414320014112 0ustar bucusers#!/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 traceroute-2.0.20/include/0000755000014400001440000000000012205150074014053 5ustar bucuserstraceroute-2.0.20/include/version.h0000644000014400001440000000027612221563064015723 0ustar bucusers/* Copyright (c) 2006, 2007 Dmitry Butskoy License: GPL v2 or any later See COPYING for the status of this software. */ #include "../VERSION" traceroute-2.0.20/Make.rules0000644000014400001440000000770412124574320014375 0ustar bucusers# # 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)) 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) ifneq ($(gcc),) CPATH = $(subst $(empty) ,:,$(includes)) LIBRARY_PATH = $(subst $(empty) ,:,$(libraries)) export CPATH LIBRARY_PATH else override CPPFLAGS += $(patsubst %,-I%,$(includes)) 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 # traceroute-2.0.20/COPYING.LIB0000644000014400001440000006364212242712216014106 0ustar bucusers 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! traceroute-2.0.20/Make.defines0000644000014400001440000000220512207144374014653 0ustar bucusers# 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 traceroute-2.0.20/default.rules0000644000014400001440000000677012213354426015150 0ustar bucusers# # 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 traceroute-2.0.20/ChangeLog0000644000014400001440000002513212347115730014214 0ustar bucusers2014-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