diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e9c8952 --- /dev/null +++ b/.gitignore @@ -0,0 +1,26 @@ +*~ +*.o + +*.diff +*.orig +*.patch +*.plist +*.rej + +core +.gdb_history +.gdbinit + +# Makefile build +/arping +/clockdiff +/ping +/ping6 +/rarpd +/rdisc +/tftpd +/tracepath +/tracepath6 +/traceroute6 +# Meson build +/builddir diff --git a/.rpmtmp/.placeholder b/.rpmtmp/.placeholder new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/.rpmtmp/.placeholder diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..8787eed --- /dev/null +++ b/.travis.yml @@ -0,0 +1,19 @@ +language: c +dist: trusty +before_install: + - sudo apt-get update -qq +install: + - sudo apt-get install libcap-dev libidn2-0-dev -qq + - sudo apt-get install nettle-dev libidn2-0-dev -qq + +compiler: + - gcc + - clang +env: + matrix: + - USE_CAP=yes + - USE_CAP=no + - USE_IDN=yes + - USE_IDN=no +script: make USE_CAP=${USE_CAP} USE_IDN=${USE_IDN} + diff --git a/INSTALL.md b/INSTALL.md new file mode 100644 index 0000000..5c6faf5 --- /dev/null +++ b/INSTALL.md @@ -0,0 +1,26 @@ +# Installation instructions +Run the following commands: + + make + make html + make man + lynx doc/iputils.html + # Read... + +## Troubleshooting +If the first `make` fails, no problems: + + make html + lynx doc/iputils.html + Read section "Installation notes"... + +But if `make html` fails too, check that DocBook package is installed +on your machine. If it is installed, and `make` does not work nevertheless, +please [open an issue on github.com] +(https://github.com/iputils/iputils/issues/new). + +## Install into a prefix +There's no `configure` option to install into a prefix. Use the `DESTDIR` +`make` variable to change the installation target. There's no support for +picking up build dependencies in a prefix. + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4fb78d5 --- /dev/null +++ b/LICENSE @@ -0,0 +1,9 @@ +arping: GPL v2 or later +clockdiff: BSD-3 +ninfod: BSD-3 +ping: BSD-3 +rarp: GPL v2 or later +rdisc: AS-IS, SUN MICROSYSTEMS license +tftpd: BSD-3 +tracepath: GPL v2 or later +traceroute: BSD-3 diff --git a/LICENSE.BSD3 b/LICENSE.BSD3 new file mode 100644 index 0000000..731a737 --- /dev/null +++ b/LICENSE.BSD3 @@ -0,0 +1,9 @@ +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/LICENSE.GPL2 b/LICENSE.GPL2 new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/LICENSE.GPL2 @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..17fc5c9 --- /dev/null +++ b/Makefile @@ -0,0 +1,233 @@ +# +# Configuration +# + +# Path to parent kernel include files directory +LIBC_INCLUDE=/usr/include +# Libraries +ADDLIB= +# Linker flags +LDFLAG_STATIC=-Wl,-Bstatic +LDFLAG_DYNAMIC=-Wl,-Bdynamic +LDFLAG_CAP=-lcap +LDFLAG_GCRYPT=-lgcrypt -lgpg-error +LDFLAG_NETTLE=-lnettle +LDFLAG_CRYPTO=-lcrypto +LDFLAG_IDN=-lidn2 +LDFLAG_RESOLV=-lresolv +LDFLAG_RT=-lrt +LDFLAG_M=-lm + +# +# Options +# + +# Capability support (with libcap) [yes|static|no] +USE_CAP=yes +# sysfs support +USE_SYSFS=yes +# IDN support [yes|no|static] +USE_IDN=yes + +# Do not use getifaddrs [no|yes|static] +WITHOUT_IFADDRS=no +# arping default device (e.g. eth0) [] +ARPING_DEFAULT_DEVICE= + +# nettle library for ipv6 ping [yes|no|static] +USE_NETTLE=yes +# libgcrypt library for ipv6 ping [no|yes|static] +USE_GCRYPT=no +# Crypto library for ping6 [shared|static|no] +USE_CRYPTO=shared +# Resolv library for ping6 [yes|static] +USE_RESOLV=yes +# ping6 source routing (deprecated by RFC5095) [no|yes|RFC3542] +ENABLE_PING6_RTHDR=no + +# rdisc server (-r option) support [no|yes] +ENABLE_RDISC_SERVER=no + +# ------------------------------------- +# What a pity, all new gccs are buggy and -Werror does not work. Sigh. +# CFLAGS+=-fno-strict-aliasing -Wstrict-prototypes -Wall -Werror -g +CFLAGS?=-O3 -g +CFLAGS+=-fno-strict-aliasing -Wstrict-prototypes -Wall +CPPFLAGS+=-D_GNU_SOURCE +LDLIB= + +FUNC_LIB = $(if $(filter static,$(1)),$(LDFLAG_STATIC) $(2) $(LDFLAG_DYNAMIC),$(2)) + +# USE_GCRYPT: DEF_GCRYPT, LIB_GCRYPT +# USE_CRYPTO: LIB_CRYPTO +ifneq ($(USE_GCRYPT),no) + LIB_CRYPTO = $(call FUNC_LIB,$(USE_GCRYPT),$(LDFLAG_GCRYPT)) + DEF_CRYPTO = -DUSE_GCRYPT +else +ifneq ($(USE_NETTLE),no) + LIB_CRYPTO = $(call FUNC_LIB,$(USE_NETTLE),$(LDFLAG_NETTLE)) + DEF_CRYPTO = -DUSE_NETTLE +else +ifneq ($(USE_CRYPTO),no) + LIB_CRYPTO = $(call FUNC_LIB,$(USE_CRYPTO),$(LDFLAG_CRYPTO)) + DEF_CRYPTO = -DUSE_OPENSSL +endif +endif +endif + +# USE_RESOLV: LIB_RESOLV +LIB_RESOLV = $(call FUNC_LIB,$(USE_RESOLV),$(LDFLAG_RESOLV)) + +# USE_CAP: DEF_CAP, LIB_CAP +ifneq ($(USE_CAP),no) + DEF_CAP = -DCAPABILITIES + LIB_CAP = $(call FUNC_LIB,$(USE_CAP),$(LDFLAG_CAP)) +endif + +# USE_SYSFS: DEF_SYSFS, LIB_SYSFS +ifneq ($(USE_SYSFS),no) + DEF_SYSFS = -DUSE_SYSFS +endif + +# USE_IDN: DEF_IDN, LIB_IDN +ifneq ($(USE_IDN),no) + DEF_IDN = -DUSE_IDN + LIB_IDN = $(call FUNC_LIB,$(USE_IDN),$(LDFLAG_IDN)) +endif + +# WITHOUT_IFADDRS: DEF_WITHOUT_IFADDRS +ifneq ($(WITHOUT_IFADDRS),no) + DEF_WITHOUT_IFADDRS = -DWITHOUT_IFADDRS +endif + +# ENABLE_RDISC_SERVER: DEF_ENABLE_RDISC_SERVER +ifneq ($(ENABLE_RDISC_SERVER),no) + DEF_ENABLE_RDISC_SERVER = -DRDISC_SERVER +endif + +# ENABLE_PING6_RTHDR: DEF_ENABLE_PING6_RTHDR +ifneq ($(ENABLE_PING6_RTHDR),no) + DEF_ENABLE_PING6_RTHDR = -DPING6_ENABLE_RTHDR +ifeq ($(ENABLE_PING6_RTHDR),RFC3542) + DEF_ENABLE_PING6_RTHDR += -DPINR6_ENABLE_RTHDR_RFC3542 +endif +endif + +# ------------------------------------- +TARGETS=ping tracepath traceroute6 clockdiff rdisc arping tftpd rarpd + +LDLIBS=$(LDLIB) $(ADDLIB) + +TODAY=$(shell date +%Y-%m-%d) +DATE=$(shell date -d $(TODAY) +%Y%m%d) +TAG:=$(shell date -d $(TODAY) +s%Y%m%d) + + +# ------------------------------------- +.PHONY: all ninfod clean distclean man html snapshot + +all: $(TARGETS) + +%.s: %.c + $(COMPILE.c) $< $(DEF_$(patsubst %.o,%,$@)) -S -o $@ +%.o: %.c + $(COMPILE.c) $< $(DEF_$(patsubst %.o,%,$@)) -o $@ +LINK.o += $(CFLAGS) +$(TARGETS): %: %.o + $(LINK.o) $^ $(LIB_$@) $(LDLIBS) -o $@ + +# ------------------------------------- +# arping +DEF_arping = $(DEF_SYSFS) $(DEF_CAP) $(DEF_IDN) $(DEF_WITHOUT_IFADDRS) +LIB_arping = $(LIB_CAP) $(LIB_IDN) $(LDFLAG_RT) + +ifneq ($(ARPING_DEFAULT_DEVICE),) +DEF_arping += -DDEFAULT_DEVICE=\"$(ARPING_DEFAULT_DEVICE)\" +endif + +# clockdiff +DEF_clockdiff = $(DEF_CAP) +LIB_clockdiff = $(LIB_CAP) + +# ping / ping6 +DEF_ping = $(DEF_CAP) $(DEF_IDN) $(DEF_CRYPTO) $(DEF_WITHOUT_IFADDRS) +DEF_ping_common = $(DEF_ping) +DEF_ping6_common = $(DEF_ping) +LIB_ping = $(LIB_CAP) $(LIB_IDN) $(LIB_CRYPTO) $(LIB_RESOLV) $(LDFLAG_M) + +ping: ping_common.o ping6_common.o +ping.o ping_common.o ping6_common.o: ping.h in6_flowlabel.h +ping6.o ping6_common.o: ping.h in6_flowlabel.h + +# rarpd +DEF_rarpd = +LIB_rarpd = + +# rdisc +DEF_rdisc = $(DEF_ENABLE_RDISC_SERVER) +LIB_rdisc = + +# tracepath +DEF_tracepath = $(DEF_IDN) +LIB_tracepath = $(LIB_IDN) + +# traceroute6 +DEF_traceroute6 = $(DEF_CAP) $(DEF_IDN) +LIB_traceroute6 = $(LIB_CAP) $(LIB_IDN) + +# tftpd +DEF_tftpd = +DEF_tftpsubs = +LIB_tftpd = + +tftpd: tftpsubs.o +tftpd.o tftpsubs.o: tftp.h + +# ------------------------------------- +# ninfod +ninfod: + @set -e; \ + if [ ! -f ninfod/Makefile ]; then \ + cd ninfod; \ + ./configure; \ + cd ..; \ + fi; \ + $(MAKE) -C ninfod + +# ------------------------------------- +man: + $(MAKE) -C doc man + +html: + $(MAKE) -C doc html + +clean: + @rm -f *.o $(TARGETS) + @$(MAKE) -C Modules clean + @$(MAKE) -C doc clean + @set -e; \ + if [ -f ninfod/Makefile ]; then \ + $(MAKE) -C ninfod clean; \ + fi + +distclean: clean + @set -e; \ + if [ -f ninfod/Makefile ]; then \ + $(MAKE) -C ninfod distclean; \ + fi + +# ------------------------------------- +RPMBUILD=rpmbuild +RPMTMP=.rpmtmp +snapshot: + @echo "#define SNAPSHOT \"$(TAG)\"" > SNAPSHOT.h + @$(MAKE) man + @git commit -a -m "iputils-$(TAG)" + @git tag -s -m "iputils-$(TAG)" $(TAG) + @git archive --format=tar --prefix=iputils-$(TAG)/ $(TAG) | bzip2 -9 > ../iputils-$(TAG).tar.bz2 + +rpm: + @git archive --format=tar --prefix=iputils/ HEAD | bzip2 -9 > $(RPMTMP)/iputils.tar.bz2 + @$(RPMBUILD) -ta --define 'current yes' $(RPMTMP)/iputils.tar.bz2 + @rm -f $(RPMTMP)/iputils.tar.bz2 + diff --git a/Modules/Makefile b/Modules/Makefile new file mode 100644 index 0000000..eb84d21 --- /dev/null +++ b/Modules/Makefile @@ -0,0 +1,12 @@ +KERNEL_INCLUDE=/usr/src/linux/include + + +CC=gcc +CCOPT=-O2 -Wstrict-prototypes -Wall -Werror -fno-strict-aliasing -fno-common +CFLAGS=-DMODULE -D__KERNEL__ -I$(KERNEL_INCLUDE) $(CCOPT) + + +all: pg3.o + +clean: + @rm -f *.o diff --git a/Modules/pg3.c b/Modules/pg3.c new file mode 100644 index 0000000..73e88f9 --- /dev/null +++ b/Modules/pg3.c @@ -0,0 +1,735 @@ +/* pg3.c: Packet Generator for packet performance testing. + * + * Copyright 2001 by Robert Olsson + * Uppsala University, Sweden + * + * 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. + * + * + * + */ + +/* + +A tool for loading a network with a preconfigurated packets. The tool is +implemented as a linux module. Parameters as output device IPG interpacket +packet, number of packets can be configured. pg uses already intalled +device driver output routine. + + +Additional hacking by: + +Jens.Laas@data.slu.se +Improved by ANK. 010120. +Improved by ANK even more. 010212. +MAC address typo fixed. 010417 --ro + + +TODO: +* could release kernel lock yet. + + +HOWTO: + +1. Compile module pg3.o and install it in the place where modprobe may find it. +2. Cut script "ipg" (see below). +3. Edit script to set preferred device and destination IP address. +4. . ipg +5. After this two commands are defined: + A. "pg" to start generator and to get results. + B. "pgset" to change generator parameters. F.e. + pgset "pkt_size 9014" sets packet size to 9014 + pgset "frags 5" packet will consist of 5 fragments + pgset "count 200000" sets number of packets to send + pgset "ipg 5000" sets artificial gap inserted between packets + to 5000 nanoseconds + pgset "dst 10.0.0.1" sets IP destination address + (BEWARE! This generator is very aggressive!) + pgset "dstmac 00:00:00:00:00:00" sets MAC destination address + pgset stop aborts injection + + Also, ^C aborts generator. + +---- cut here + +#! /bin/sh + +modprobe pg3.o + +function pgset() { + local result + + echo $1 > /proc/net/pg + + result=`cat /proc/net/pg | fgrep "Result: OK:"` + if [ "$result" = "" ]; then + cat /proc/net/pg | fgrep Result: + fi +} + +function pg() { + echo inject > /proc/net/pg + cat /proc/net/pg +} + +pgset "odev eth0" +pgset "dst 0.0.0.0" + +---- cut here +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static char version[] __initdata = + "pg3.c: v1.0 010812: Packet Generator for packet performance testing.\n"; + + + +/* Parameters */ + +char pg_outdev[32], pg_dst[32]; +int pkt_size=ETH_ZLEN; +int nfrags=0; +__u32 pg_count = 100000; /* Default No packets to send */ +__u32 pg_ipg = 0; /* Default Interpacket gap in nsec */ + +/* Globar vars */ + +int debug; +int forced_stop; +int pg_cpu_speed; +int pg_busy; + +static __u8 hh[14] = { + 0x00, 0x80, 0xC8, 0x79, 0xB3, 0xCB, + + /* We fill in SRC address later */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00 +}; + +unsigned char *pg_dstmac = hh; +char pg_result[512]; + + +static struct net_device *pg_setup_inject(u32 *saddrp) +{ + int p1, p2; + struct net_device *odev; + u32 saddr; + + rtnl_lock(); + odev = __dev_get_by_name(pg_outdev); + if (!odev) { + sprintf(pg_result, "No such netdevice: \"%s\"", pg_outdev); + goto out_unlock; + } + + if (odev->type != ARPHRD_ETHER) { + sprintf(pg_result, "Not ethernet device: \"%s\"", pg_outdev); + goto out_unlock; + } + + if (!netif_running(odev)) { + sprintf(pg_result, "Device is down: \"%s\"", pg_outdev); + goto out_unlock; + } + + for(p1=6,p2=0; p1 < odev->addr_len+6;p1++) + hh[p1]=odev->dev_addr[p2++]; + + saddr = 0; + if (odev->ip_ptr) { + struct in_device *in_dev = odev->ip_ptr; + + if (in_dev->ifa_list) + saddr = in_dev->ifa_list->ifa_address; + } + atomic_inc(&odev->refcnt); + rtnl_unlock(); + + *saddrp = saddr; + return odev; + +out_unlock: + rtnl_unlock(); + return NULL; +} + + +u32 idle_acc_lo, idle_acc_hi; + +void nanospin(int pg_ipg) +{ + u32 idle_start, idle; + + idle_start = get_cycles(); + + for (;;) { + barrier(); + idle = get_cycles() - idle_start; + if (idle*1000 >= pg_ipg*pg_cpu_speed) + break; + } + idle_acc_lo += idle; + if (idle_acc_lo < idle) + idle_acc_hi++; +} + +int calc_mhz(void) +{ + struct timeval start, stop; + u32 start_s, elapsed; + + do_gettimeofday(&start); + start_s = get_cycles(); + do { + barrier(); + elapsed = get_cycles() - start_s; + if (elapsed == 0) + return 0; + } while (elapsed < 1000*50000); + do_gettimeofday(&stop); + return elapsed/(stop.tv_usec-start.tv_usec+1000000*(stop.tv_sec-start.tv_sec)); +} + +static void cycles_calibrate(void) +{ + int i; + + for (i=0; i<3; i++) { + int res = calc_mhz(); + if (res > pg_cpu_speed) + pg_cpu_speed = res; + } +} + +struct sk_buff * +fill_packet(struct net_device *odev, __u32 saddr) +{ + struct sk_buff *skb; + __u8 *eth; + struct udphdr *udph; + int datalen, iplen; + struct iphdr *iph; + + skb = alloc_skb(pkt_size+64+16, GFP_ATOMIC); + if (!skb) { + sprintf(pg_result, "No memory"); + return NULL; + } + + skb_reserve(skb, 16); + + /* Reserve for ethernet and IP header */ + eth = (__u8 *) skb_push(skb, 14); + iph = (struct iphdr*)skb_put(skb, sizeof( struct iphdr)); + udph = (struct udphdr*)skb_put(skb, sizeof( struct udphdr)); + + /* Copy the ethernet header */ + memcpy(eth, hh, 14); + + datalen = pkt_size-14-20-8; /* Eth + IPh + UDPh */ + if (datalen < 0) + datalen = 0; + + udph->source= htons(9); + udph->dest= htons(9); + udph->len= htons(datalen+8); /* DATA + udphdr */ + udph->check=0; /* No checksum */ + + iph->ihl=5; + iph->version=4; + iph->ttl=3; + iph->tos=0; + iph->protocol = IPPROTO_UDP; /* UDP */ + iph->saddr = saddr; + iph->daddr = in_aton(pg_dst); + iph->frag_off = 0; + iplen = 20 + 8 + datalen; + iph->tot_len = htons(iplen); + iph->check = 0; + iph->check = ip_fast_csum((void *)iph, iph->ihl); + skb->protocol = __constant_htons(ETH_P_IP); + skb->mac.raw = ((u8*)iph) - 14; + skb->dev = odev; + skb->pkt_type = PACKET_HOST; + + if (nfrags<=0) { + skb_put(skb, datalen); + } else { + int frags = nfrags; + int i; + + if (frags > MAX_SKB_FRAGS) + frags = MAX_SKB_FRAGS; + if (datalen > frags*PAGE_SIZE) { + skb_put(skb, datalen-frags*PAGE_SIZE); + datalen = frags*PAGE_SIZE; + } + + i = 0; + while (datalen > 0) { + struct page *page = alloc_pages(GFP_KERNEL, 0); + skb_shinfo(skb)->frags[i].page = page; + skb_shinfo(skb)->frags[i].page_offset = 0; + skb_shinfo(skb)->frags[i].size = (datalen < PAGE_SIZE ? datalen : PAGE_SIZE); + datalen -= skb_shinfo(skb)->frags[i].size; + skb->len += skb_shinfo(skb)->frags[i].size; + skb->data_len += skb_shinfo(skb)->frags[i].size; + i++; + skb_shinfo(skb)->nr_frags = i; + } + + while (i < frags) { + int rem; + + if (i == 0) + break; + + rem = skb_shinfo(skb)->frags[i-1].size/2; + if (rem == 0) + break; + + skb_shinfo(skb)->frags[i-1].size -= rem; + + skb_shinfo(skb)->frags[i] = skb_shinfo(skb)->frags[i-1]; + get_page(skb_shinfo(skb)->frags[i].page); + skb_shinfo(skb)->frags[i].page = skb_shinfo(skb)->frags[i-1].page; + skb_shinfo(skb)->frags[i].page_offset += skb_shinfo(skb)->frags[i-1].size; + skb_shinfo(skb)->frags[i].size = rem; + i++; + skb_shinfo(skb)->nr_frags = i; + } + } + + return skb; +} + + +static void pg_inject(void) +{ + u32 saddr; + struct net_device *odev; + struct sk_buff *skb; + struct timeval start, stop; + u32 total, idle; + int pc, lcount; + + odev = pg_setup_inject(&saddr); + if (!odev) + return; + + skb = fill_packet(odev, saddr); + if (skb == NULL) + goto out_reldev; + + forced_stop = 0; + idle_acc_hi = 0; + idle_acc_lo = 0; + pc = 0; + lcount = pg_count; + do_gettimeofday(&start); + + for(;;) { + spin_lock_bh(&odev->xmit_lock); + atomic_inc(&skb->users); + if (!netif_queue_stopped(odev)) { + if (odev->hard_start_xmit(skb, odev)) { + kfree_skb(skb); + if (net_ratelimit()) + printk(KERN_INFO "Hard xmit error\n"); + } + pc++; + } else { + kfree_skb(skb); + } + spin_unlock_bh(&odev->xmit_lock); + + if (pg_ipg) + nanospin(pg_ipg); + if (forced_stop) + goto out_intr; + if (signal_pending(current)) + goto out_intr; + + if (--lcount == 0) { + if (atomic_read(&skb->users) != 1) { + u32 idle_start, idle; + + idle_start = get_cycles(); + while (atomic_read(&skb->users) != 1) { + if (signal_pending(current)) + goto out_intr; + schedule(); + } + idle = get_cycles() - idle_start; + idle_acc_lo += idle; + if (idle_acc_lo < idle) + idle_acc_hi++; + } + break; + } + + if (netif_queue_stopped(odev) || current->need_resched) { + u32 idle_start, idle; + + idle_start = get_cycles(); + do { + if (signal_pending(current)) + goto out_intr; + if (!netif_running(odev)) + goto out_intr; + if (current->need_resched) + schedule(); + else + do_softirq(); + } while (netif_queue_stopped(odev)); + idle = get_cycles() - idle_start; + idle_acc_lo += idle; + if (idle_acc_lo < idle) + idle_acc_hi++; + } + } + + do_gettimeofday(&stop); + + total = (stop.tv_sec - start.tv_sec)*1000000 + + stop.tv_usec - start.tv_usec; + + idle = (((idle_acc_hi<<20)/pg_cpu_speed)<<12)+idle_acc_lo/pg_cpu_speed; + + if (1) { + char *p = pg_result; + + p += sprintf(p, "OK: %u(c%u+d%u) usec, %u (%dbyte,%dfrags) %upps %uMB/sec", + total, total-idle, idle, + pc, skb->len, skb_shinfo(skb)->nr_frags, + ((pc*1000)/(total/1000)), + (((pc*1000)/(total/1000))*pkt_size)/1024/1024 + ); + } + +out_relskb: + kfree_skb(skb); +out_reldev: + dev_put(odev); + return; + +out_intr: + sprintf(pg_result, "Interrupted"); + goto out_relskb; +} + +/* proc/net/pg */ + +static struct proc_dir_entry *pg_proc_ent = 0; +static struct proc_dir_entry *pg_busy_proc_ent = 0; + +int proc_pg_busy_read(char *buf , char **start, off_t offset, + int len, int *eof, void *data) +{ + char *p; + + p = buf; + p += sprintf(p, "%d\n", pg_busy); + *eof = 1; + + return p-buf; +} + +int proc_pg_read(char *buf , char **start, off_t offset, + int len, int *eof, void *data) +{ + char *p; + int i; + + p = buf; + p += sprintf(p, "Params: count=%u pkt_size=%u frags %d ipg %u odev \"%s\" dst %s dstmac ", + pg_count, pkt_size, nfrags, pg_ipg, + pg_outdev, pg_dst); + for(i=0;i<6;i++) + p += sprintf(p, "%02X%s", pg_dstmac[i], i == 5 ? "\n" : ":"); + + if(pg_result[0]) + p += sprintf(p, "Result: %s\n", pg_result); + else + p += sprintf(p, "Result: Idle\n"); + *eof = 1; + return p-buf; +} + +int count_trail_chars(const char *buffer, unsigned int maxlen) +{ + int i; + + for(i=0; i= '0' && *v <= '9') { + *m *= 16; + *m += *v - '0'; + } + if(*v >= 'A' && *v <= 'F') { + *m *= 16; + *m += *v - 'A' + 10; + } + if(*v >= 'a' && *v <= 'f') { + *m *= 16; + *m += *v - 'a' + 10; + } + if(*v == ':') { + m++; + *m = 0; + } + } + sprintf(pg_result, "OK: dstmac"); + return count; + } + + if (!strcmp(name, "inject") || !strcmp(name, "start") ) { + MOD_INC_USE_COUNT; + pg_busy = 1; + strcpy(pg_result, "Starting"); + pg_inject(); + pg_busy = 0; + MOD_DEC_USE_COUNT; + return count; + } + + sprintf(pg_result, "No such parameter \"%s\"", name); + return -EINVAL; +} + +static int pg_init(void) +{ + printk(version); + cycles_calibrate(); + if (pg_cpu_speed == 0) { + printk("pg3: Error: your machine does not have working cycle counter.\n"); + return -EINVAL; + } + if(!pg_proc_ent) { + pg_proc_ent = create_proc_entry("net/pg", 0600, 0); + if (pg_proc_ent) { + pg_proc_ent->read_proc = proc_pg_read; + pg_proc_ent->write_proc = proc_pg_write; + pg_proc_ent->data = 0; + } + pg_busy_proc_ent = create_proc_entry("net/pg_busy", 0, 0); + if (pg_busy_proc_ent) { + pg_busy_proc_ent->read_proc = proc_pg_busy_read; + pg_busy_proc_ent->data = 0; + } + } + return 0; +} + +void pg_cleanup(void) +{ + if (pg_proc_ent) { + remove_proc_entry("net/pg", NULL); + pg_proc_ent = 0; + remove_proc_entry("net/pg_busy", NULL); + pg_busy_proc_ent = 0; + } +} + +module_init(pg_init); +module_exit(pg_cleanup); + + +#if LINUX_VERSION_CODE > 0x20118 +MODULE_AUTHOR("Robert Olsson +Corrections, HA extensions : 2000/10/03-15 : + - Willy Tarreau + - Constantine Gavrilov + - Chad N. Tindel + - Janice Girouard + - Jay Vosburgh + +Reorganized and updated Feb 2005 by Jay Vosburgh +Added Sysfs information: 2006/04/24 + - Mitch Williams + +Introduction +============ + + The Linux bonding driver provides a method for aggregating +multiple network interfaces into a single logical "bonded" interface. +The behavior of the bonded interfaces depends upon the mode; generally +speaking, modes provide either hot standby or load balancing services. +Additionally, link integrity monitoring may be performed. + + The bonding driver originally came from Donald Becker's +beowulf patches for kernel 2.0. It has changed quite a bit since, and +the original tools from extreme-linux and beowulf sites will not work +with this version of the driver. + + For new versions of the driver, updated userspace tools, and +who to ask for help, please follow the links at the end of this file. + +Table of Contents +================= + +1. Bonding Driver Installation + +2. Bonding Driver Options + +3. Configuring Bonding Devices +3.1 Configuration with Sysconfig Support +3.1.1 Using DHCP with Sysconfig +3.1.2 Configuring Multiple Bonds with Sysconfig +3.2 Configuration with Initscripts Support +3.2.1 Using DHCP with Initscripts +3.2.2 Configuring Multiple Bonds with Initscripts +3.3 Configuring Bonding Manually with Ifenslave +3.3.1 Configuring Multiple Bonds Manually +3.4 Configuring Bonding Manually via Sysfs +3.5 Configuration with Interfaces Support +3.6 Overriding Configuration for Special Cases + +4. Querying Bonding Configuration +4.1 Bonding Configuration +4.2 Network Configuration + +5. Switch Configuration + +6. 802.1q VLAN Support + +7. Link Monitoring +7.1 ARP Monitor Operation +7.2 Configuring Multiple ARP Targets +7.3 MII Monitor Operation + +8. Potential Trouble Sources +8.1 Adventures in Routing +8.2 Ethernet Device Renaming +8.3 Painfully Slow Or No Failed Link Detection By Miimon + +9. SNMP agents + +10. Promiscuous mode + +11. Configuring Bonding for High Availability +11.1 High Availability in a Single Switch Topology +11.2 High Availability in a Multiple Switch Topology +11.2.1 HA Bonding Mode Selection for Multiple Switch Topology +11.2.2 HA Link Monitoring for Multiple Switch Topology + +12. Configuring Bonding for Maximum Throughput +12.1 Maximum Throughput in a Single Switch Topology +12.1.1 MT Bonding Mode Selection for Single Switch Topology +12.1.2 MT Link Monitoring for Single Switch Topology +12.2 Maximum Throughput in a Multiple Switch Topology +12.2.1 MT Bonding Mode Selection for Multiple Switch Topology +12.2.2 MT Link Monitoring for Multiple Switch Topology + +13. Switch Behavior Issues +13.1 Link Establishment and Failover Delays +13.2 Duplicated Incoming Packets + +14. Hardware Specific Considerations +14.1 IBM BladeCenter + +15. Frequently Asked Questions + +16. Resources and Links + + +1. Bonding Driver Installation +============================== + + Most popular distro kernels ship with the bonding driver +already available as a module and the ifenslave user level control +program installed and ready for use. If your distro does not, or you +have need to compile bonding from source (e.g., configuring and +installing a mainline kernel from kernel.org), you'll need to perform +the following steps: + +1.1 Configure and build the kernel with bonding +----------------------------------------------- + + The current version of the bonding driver is available in the +drivers/net/bonding subdirectory of the most recent kernel source +(which is available on http://kernel.org). Most users "rolling their +own" will want to use the most recent kernel from kernel.org. + + Configure kernel with "make menuconfig" (or "make xconfig" or +"make config"), then select "Bonding driver support" in the "Network +device support" section. It is recommended that you configure the +driver as module since it is currently the only way to pass parameters +to the driver or configure more than one bonding device. + + Build and install the new kernel and modules, then continue +below to install ifenslave. + +1.2 Install ifenslave Control Utility +------------------------------------- + + The ifenslave user level control program is included in the +kernel source tree, in the file Documentation/networking/ifenslave.c. +It is generally recommended that you use the ifenslave that +corresponds to the kernel that you are using (either from the same +source tree or supplied with the distro), however, ifenslave +executables from older kernels should function (but features newer +than the ifenslave release are not supported). Running an ifenslave +that is newer than the kernel is not supported, and may or may not +work. + + To install ifenslave, do the following: + +# gcc -Wall -O -I/usr/src/linux/include ifenslave.c -o ifenslave +# cp ifenslave /sbin/ifenslave + + If your kernel source is not in "/usr/src/linux," then replace +"/usr/src/linux/include" in the above with the location of your kernel +source include directory. + + You may wish to back up any existing /sbin/ifenslave, or, for +testing or informal use, tag the ifenslave to the kernel version +(e.g., name the ifenslave executable /sbin/ifenslave-2.6.10). + +IMPORTANT NOTE: + + If you omit the "-I" or specify an incorrect directory, you +may end up with an ifenslave that is incompatible with the kernel +you're trying to build it for. Some distros (e.g., Red Hat from 7.1 +onwards) do not have /usr/include/linux symbolically linked to the +default kernel source include directory. + +SECOND IMPORTANT NOTE: + If you plan to configure bonding using sysfs or using the +/etc/network/interfaces file, you do not need to use ifenslave. + +2. Bonding Driver Options +========================= + + Options for the bonding driver are supplied as parameters to the +bonding module at load time, or are specified via sysfs. + + Module options may be given as command line arguments to the +insmod or modprobe command, but are usually specified in either the +/etc/modrobe.d/*.conf configuration files, or in a distro-specific +configuration file (some of which are detailed in the next section). + + Details on bonding support for sysfs is provided in the +"Configuring Bonding Manually via Sysfs" section, below. + + The available bonding driver parameters are listed below. If a +parameter is not specified the default value is used. When initially +configuring a bond, it is recommended "tail -f /var/log/messages" be +run in a separate window to watch for bonding driver error messages. + + It is critical that either the miimon or arp_interval and +arp_ip_target parameters be specified, otherwise serious network +degradation will occur during link failures. Very few devices do not +support at least miimon, so there is really no reason not to use it. + + Options with textual values will accept either the text name +or, for backwards compatibility, the option value. E.g., +"mode=802.3ad" and "mode=4" set the same mode. + + The parameters are as follows: + +active_slave + + Specifies the new active slave for modes that support it + (active-backup, balance-alb and balance-tlb). Possible values + are the name of any currently enslaved interface, or an empty + string. If a name is given, the slave and its link must be up in order + to be selected as the new active slave. If an empty string is + specified, the current active slave is cleared, and a new active + slave is selected automatically. + + Note that this is only available through the sysfs interface. No module + parameter by this name exists. + + The normal value of this option is the name of the currently + active slave, or the empty string if there is no active slave or + the current mode does not use an active slave. + +ad_select + + Specifies the 802.3ad aggregation selection logic to use. The + possible values and their effects are: + + stable or 0 + + The active aggregator is chosen by largest aggregate + bandwidth. + + Reselection of the active aggregator occurs only when all + slaves of the active aggregator are down or the active + aggregator has no slaves. + + This is the default value. + + bandwidth or 1 + + The active aggregator is chosen by largest aggregate + bandwidth. Reselection occurs if: + + - A slave is added to or removed from the bond + + - Any slave's link state changes + + - Any slave's 802.3ad association state changes + + - The bond's administrative state changes to up + + count or 2 + + The active aggregator is chosen by the largest number of + ports (slaves). Reselection occurs as described under the + "bandwidth" setting, above. + + The bandwidth and count selection policies permit failover of + 802.3ad aggregations when partial failure of the active aggregator + occurs. This keeps the aggregator with the highest availability + (either in bandwidth or in number of ports) active at all times. + + This option was added in bonding version 3.4.0. + +all_slaves_active + + Specifies that duplicate frames (received on inactive ports) should be + dropped (0) or delivered (1). + + Normally, bonding will drop duplicate frames (received on inactive + ports), which is desirable for most users. But there are some times + it is nice to allow duplicate frames to be delivered. + + The default value is 0 (drop duplicate frames received on inactive + ports). + +arp_interval + + Specifies the ARP link monitoring frequency in milliseconds. + + The ARP monitor works by periodically checking the slave + devices to determine whether they have sent or received + traffic recently (the precise criteria depends upon the + bonding mode, and the state of the slave). Regular traffic is + generated via ARP probes issued for the addresses specified by + the arp_ip_target option. + + This behavior can be modified by the arp_validate option, + below. + + If ARP monitoring is used in an etherchannel compatible mode + (modes 0 and 2), the switch should be configured in a mode + that evenly distributes packets across all links. If the + switch is configured to distribute the packets in an XOR + fashion, all replies from the ARP targets will be received on + the same link which could cause the other team members to + fail. ARP monitoring should not be used in conjunction with + miimon. A value of 0 disables ARP monitoring. The default + value is 0. + +arp_ip_target + + Specifies the IP addresses to use as ARP monitoring peers when + arp_interval is > 0. These are the targets of the ARP request + sent to determine the health of the link to the targets. + Specify these values in ddd.ddd.ddd.ddd format. Multiple IP + addresses must be separated by a comma. At least one IP + address must be given for ARP monitoring to function. The + maximum number of targets that can be specified is 16. The + default value is no IP addresses. + +arp_validate + + Specifies whether or not ARP probes and replies should be + validated in the active-backup mode. This causes the ARP + monitor to examine the incoming ARP requests and replies, and + only consider a slave to be up if it is receiving the + appropriate ARP traffic. + + Possible values are: + + none or 0 + + No validation is performed. This is the default. + + active or 1 + + Validation is performed only for the active slave. + + backup or 2 + + Validation is performed only for backup slaves. + + all or 3 + + Validation is performed for all slaves. + + For the active slave, the validation checks ARP replies to + confirm that they were generated by an arp_ip_target. Since + backup slaves do not typically receive these replies, the + validation performed for backup slaves is on the ARP request + sent out via the active slave. It is possible that some + switch or network configurations may result in situations + wherein the backup slaves do not receive the ARP requests; in + such a situation, validation of backup slaves must be + disabled. + + This option is useful in network configurations in which + multiple bonding hosts are concurrently issuing ARPs to one or + more targets beyond a common switch. Should the link between + the switch and target fail (but not the switch itself), the + probe traffic generated by the multiple bonding instances will + fool the standard ARP monitor into considering the links as + still up. Use of the arp_validate option can resolve this, as + the ARP monitor will only consider ARP requests and replies + associated with its own instance of bonding. + + This option was added in bonding version 3.1.0. + +downdelay + + Specifies the time, in milliseconds, to wait before disabling + a slave after a link failure has been detected. This option + is only valid for the miimon link monitor. The downdelay + value should be a multiple of the miimon value; if not, it + will be rounded down to the nearest multiple. The default + value is 0. + +fail_over_mac + + Specifies whether active-backup mode should set all slaves to + the same MAC address at enslavement (the traditional + behavior), or, when enabled, perform special handling of the + bond's MAC address in accordance with the selected policy. + + Possible values are: + + none or 0 + + This setting disables fail_over_mac, and causes + bonding to set all slaves of an active-backup bond to + the same MAC address at enslavement time. This is the + default. + + active or 1 + + The "active" fail_over_mac policy indicates that the + MAC address of the bond should always be the MAC + address of the currently active slave. The MAC + address of the slaves is not changed; instead, the MAC + address of the bond changes during a failover. + + This policy is useful for devices that cannot ever + alter their MAC address, or for devices that refuse + incoming broadcasts with their own source MAC (which + interferes with the ARP monitor). + + The down side of this policy is that every device on + the network must be updated via gratuitous ARP, + vs. just updating a switch or set of switches (which + often takes place for any traffic, not just ARP + traffic, if the switch snoops incoming traffic to + update its tables) for the traditional method. If the + gratuitous ARP is lost, communication may be + disrupted. + + When this policy is used in conjunction with the mii + monitor, devices which assert link up prior to being + able to actually transmit and receive are particularly + susceptible to loss of the gratuitous ARP, and an + appropriate updelay setting may be required. + + follow or 2 + + The "follow" fail_over_mac policy causes the MAC + address of the bond to be selected normally (normally + the MAC address of the first slave added to the bond). + However, the second and subsequent slaves are not set + to this MAC address while they are in a backup role; a + slave is programmed with the bond's MAC address at + failover time (and the formerly active slave receives + the newly active slave's MAC address). + + This policy is useful for multiport devices that + either become confused or incur a performance penalty + when multiple ports are programmed with the same MAC + address. + + + The default policy is none, unless the first slave cannot + change its MAC address, in which case the active policy is + selected by default. + + This option may be modified via sysfs only when no slaves are + present in the bond. + + This option was added in bonding version 3.2.0. The "follow" + policy was added in bonding version 3.3.0. + +lacp_rate + + Option specifying the rate in which we'll ask our link partner + to transmit LACPDU packets in 802.3ad mode. Possible values + are: + + slow or 0 + Request partner to transmit LACPDUs every 30 seconds + + fast or 1 + Request partner to transmit LACPDUs every 1 second + + The default is slow. + +max_bonds + + Specifies the number of bonding devices to create for this + instance of the bonding driver. E.g., if max_bonds is 3, and + the bonding driver is not already loaded, then bond0, bond1 + and bond2 will be created. The default value is 1. Specifying + a value of 0 will load bonding, but will not create any devices. + +miimon + + Specifies the MII link monitoring frequency in milliseconds. + This determines how often the link state of each slave is + inspected for link failures. A value of zero disables MII + link monitoring. A value of 100 is a good starting point. + The use_carrier option, below, affects how the link state is + determined. See the High Availability section for additional + information. The default value is 0. + +min_links + + Specifies the minimum number of links that must be active before + asserting carrier. It is similar to the Cisco EtherChannel min-links + feature. This allows setting the minimum number of member ports that + must be up (link-up state) before marking the bond device as up + (carrier on). This is useful for situations where higher level services + such as clustering want to ensure a minimum number of low bandwidth + links are active before switchover. This option only affect 802.3ad + mode. + + The default value is 0. This will cause carrier to be asserted (for + 802.3ad mode) whenever there is an active aggregator, regardless of the + number of available links in that aggregator. Note that, because an + aggregator cannot be active without at least one available link, + setting this option to 0 or to 1 has the exact same effect. + +mode + + Specifies one of the bonding policies. The default is + balance-rr (round robin). Possible values are: + + balance-rr or 0 + + Round-robin policy: Transmit packets in sequential + order from the first available slave through the + last. This mode provides load balancing and fault + tolerance. + + active-backup or 1 + + Active-backup policy: Only one slave in the bond is + active. A different slave becomes active if, and only + if, the active slave fails. The bond's MAC address is + externally visible on only one port (network adapter) + to avoid confusing the switch. + + In bonding version 2.6.2 or later, when a failover + occurs in active-backup mode, bonding will issue one + or more gratuitous ARPs on the newly active slave. + One gratuitous ARP is issued for the bonding master + interface and each VLAN interfaces configured above + it, provided that the interface has at least one IP + address configured. Gratuitous ARPs issued for VLAN + interfaces are tagged with the appropriate VLAN id. + + This mode provides fault tolerance. The primary + option, documented below, affects the behavior of this + mode. + + balance-xor or 2 + + XOR policy: Transmit based on the selected transmit + hash policy. The default policy is a simple [(source + MAC address XOR'd with destination MAC address) modulo + slave count]. Alternate transmit policies may be + selected via the xmit_hash_policy option, described + below. + + This mode provides load balancing and fault tolerance. + + broadcast or 3 + + Broadcast policy: transmits everything on all slave + interfaces. This mode provides fault tolerance. + + 802.3ad or 4 + + IEEE 802.3ad Dynamic link aggregation. Creates + aggregation groups that share the same speed and + duplex settings. Utilizes all slaves in the active + aggregator according to the 802.3ad specification. + + Slave selection for outgoing traffic is done according + to the transmit hash policy, which may be changed from + the default simple XOR policy via the xmit_hash_policy + option, documented below. Note that not all transmit + policies may be 802.3ad compliant, particularly in + regards to the packet mis-ordering requirements of + section 43.2.4 of the 802.3ad standard. Differing + peer implementations will have varying tolerances for + noncompliance. + + Prerequisites: + + 1. Ethtool support in the base drivers for retrieving + the speed and duplex of each slave. + + 2. A switch that supports IEEE 802.3ad Dynamic link + aggregation. + + Most switches will require some type of configuration + to enable 802.3ad mode. + + balance-tlb or 5 + + Adaptive transmit load balancing: channel bonding that + does not require any special switch support. The + outgoing traffic is distributed according to the + current load (computed relative to the speed) on each + slave. Incoming traffic is received by the current + slave. If the receiving slave fails, another slave + takes over the MAC address of the failed receiving + slave. + + Prerequisite: + + Ethtool support in the base drivers for retrieving the + speed of each slave. + + balance-alb or 6 + + Adaptive load balancing: includes balance-tlb plus + receive load balancing (rlb) for IPV4 traffic, and + does not require any special switch support. The + receive load balancing is achieved by ARP negotiation. + The bonding driver intercepts the ARP Replies sent by + the local system on their way out and overwrites the + source hardware address with the unique hardware + address of one of the slaves in the bond such that + different peers use different hardware addresses for + the server. + + Receive traffic from connections created by the server + is also balanced. When the local system sends an ARP + Request the bonding driver copies and saves the peer's + IP information from the ARP packet. When the ARP + Reply arrives from the peer, its hardware address is + retrieved and the bonding driver initiates an ARP + reply to this peer assigning it to one of the slaves + in the bond. A problematic outcome of using ARP + negotiation for balancing is that each time that an + ARP request is broadcast it uses the hardware address + of the bond. Hence, peers learn the hardware address + of the bond and the balancing of receive traffic + collapses to the current slave. This is handled by + sending updates (ARP Replies) to all the peers with + their individually assigned hardware address such that + the traffic is redistributed. Receive traffic is also + redistributed when a new slave is added to the bond + and when an inactive slave is re-activated. The + receive load is distributed sequentially (round robin) + among the group of highest speed slaves in the bond. + + When a link is reconnected or a new slave joins the + bond the receive traffic is redistributed among all + active slaves in the bond by initiating ARP Replies + with the selected MAC address to each of the + clients. The updelay parameter (detailed below) must + be set to a value equal or greater than the switch's + forwarding delay so that the ARP Replies sent to the + peers will not be blocked by the switch. + + Prerequisites: + + 1. Ethtool support in the base drivers for retrieving + the speed of each slave. + + 2. Base driver support for setting the hardware + address of a device while it is open. This is + required so that there will always be one slave in the + team using the bond hardware address (the + curr_active_slave) while having a unique hardware + address for each slave in the bond. If the + curr_active_slave fails its hardware address is + swapped with the new curr_active_slave that was + chosen. + +num_grat_arp +num_unsol_na + + Specify the number of peer notifications (gratuitous ARPs and + unsolicited IPv6 Neighbor Advertisements) to be issued after a + failover event. As soon as the link is up on the new slave + (possibly immediately) a peer notification is sent on the + bonding device and each VLAN sub-device. This is repeated at + each link monitor interval (arp_interval or miimon, whichever + is active) if the number is greater than 1. + + The valid range is 0 - 255; the default value is 1. These options + affect only the active-backup mode. These options were added for + bonding versions 3.3.0 and 3.4.0 respectively. + + From Linux 3.0 and bonding version 3.7.1, these notifications + are generated by the ipv4 and ipv6 code and the numbers of + repetitions cannot be set independently. + +primary + + A string (eth0, eth2, etc) specifying which slave is the + primary device. The specified device will always be the + active slave while it is available. Only when the primary is + off-line will alternate devices be used. This is useful when + one slave is preferred over another, e.g., when one slave has + higher throughput than another. + + The primary option is only valid for active-backup mode. + +primary_reselect + + Specifies the reselection policy for the primary slave. This + affects how the primary slave is chosen to become the active slave + when failure of the active slave or recovery of the primary slave + occurs. This option is designed to prevent flip-flopping between + the primary slave and other slaves. Possible values are: + + always or 0 (default) + + The primary slave becomes the active slave whenever it + comes back up. + + better or 1 + + The primary slave becomes the active slave when it comes + back up, if the speed and duplex of the primary slave is + better than the speed and duplex of the current active + slave. + + failure or 2 + + The primary slave becomes the active slave only if the + current active slave fails and the primary slave is up. + + The primary_reselect setting is ignored in two cases: + + If no slaves are active, the first slave to recover is + made the active slave. + + When initially enslaved, the primary slave is always made + the active slave. + + Changing the primary_reselect policy via sysfs will cause an + immediate selection of the best active slave according to the new + policy. This may or may not result in a change of the active + slave, depending upon the circumstances. + + This option was added for bonding version 3.6.0. + +updelay + + Specifies the time, in milliseconds, to wait before enabling a + slave after a link recovery has been detected. This option is + only valid for the miimon link monitor. The updelay value + should be a multiple of the miimon value; if not, it will be + rounded down to the nearest multiple. The default value is 0. + +use_carrier + + Specifies whether or not miimon should use MII or ETHTOOL + ioctls vs. netif_carrier_ok() to determine the link + status. The MII or ETHTOOL ioctls are less efficient and + utilize a deprecated calling sequence within the kernel. The + netif_carrier_ok() relies on the device driver to maintain its + state with netif_carrier_on/off; at this writing, most, but + not all, device drivers support this facility. + + If bonding insists that the link is up when it should not be, + it may be that your network device driver does not support + netif_carrier_on/off. The default state for netif_carrier is + "carrier on," so if a driver does not support netif_carrier, + it will appear as if the link is always up. In this case, + setting use_carrier to 0 will cause bonding to revert to the + MII / ETHTOOL ioctl method to determine the link state. + + A value of 1 enables the use of netif_carrier_ok(), a value of + 0 will use the deprecated MII / ETHTOOL ioctls. The default + value is 1. + +xmit_hash_policy + + Selects the transmit hash policy to use for slave selection in + balance-xor and 802.3ad modes. Possible values are: + + layer2 + + Uses XOR of hardware MAC addresses to generate the + hash. The formula is + + (source MAC XOR destination MAC) modulo slave count + + This algorithm will place all traffic to a particular + network peer on the same slave. + + This algorithm is 802.3ad compliant. + + layer2+3 + + This policy uses a combination of layer2 and layer3 + protocol information to generate the hash. + + Uses XOR of hardware MAC addresses and IP addresses to + generate the hash. The formula is + + (((source IP XOR dest IP) AND 0xffff) XOR + ( source MAC XOR destination MAC )) + modulo slave count + + This algorithm will place all traffic to a particular + network peer on the same slave. For non-IP traffic, + the formula is the same as for the layer2 transmit + hash policy. + + This policy is intended to provide a more balanced + distribution of traffic than layer2 alone, especially + in environments where a layer3 gateway device is + required to reach most destinations. + + This algorithm is 802.3ad compliant. + + layer3+4 + + This policy uses upper layer protocol information, + when available, to generate the hash. This allows for + traffic to a particular network peer to span multiple + slaves, although a single connection will not span + multiple slaves. + + The formula for unfragmented TCP and UDP packets is + + ((source port XOR dest port) XOR + ((source IP XOR dest IP) AND 0xffff) + modulo slave count + + For fragmented TCP or UDP packets and all other IP + protocol traffic, the source and destination port + information is omitted. For non-IP traffic, the + formula is the same as for the layer2 transmit hash + policy. + + This policy is intended to mimic the behavior of + certain switches, notably Cisco switches with PFC2 as + well as some Foundry and IBM products. + + This algorithm is not fully 802.3ad compliant. A + single TCP or UDP conversation containing both + fragmented and unfragmented packets will see packets + striped across two interfaces. This may result in out + of order delivery. Most traffic types will not meet + this criteria, as TCP rarely fragments traffic, and + most UDP traffic is not involved in extended + conversations. Other implementations of 802.3ad may + or may not tolerate this noncompliance. + + The default value is layer2. This option was added in bonding + version 2.6.3. In earlier versions of bonding, this parameter + does not exist, and the layer2 policy is the only policy. The + layer2+3 value was added for bonding version 3.2.2. + +resend_igmp + + Specifies the number of IGMP membership reports to be issued after + a failover event. One membership report is issued immediately after + the failover, subsequent packets are sent in each 200ms interval. + + The valid range is 0 - 255; the default value is 1. A value of 0 + prevents the IGMP membership report from being issued in response + to the failover event. + + This option is useful for bonding modes balance-rr (0), active-backup + (1), balance-tlb (5) and balance-alb (6), in which a failover can + switch the IGMP traffic from one slave to another. Therefore a fresh + IGMP report must be issued to cause the switch to forward the incoming + IGMP traffic over the newly selected slave. + + This option was added for bonding version 3.7.0. + +3. Configuring Bonding Devices +============================== + + You can configure bonding using either your distro's network +initialization scripts, or manually using either ifenslave or the +sysfs interface. Distros generally use one of three packages for the +network initialization scripts: initscripts, sysconfig or interfaces. +Recent versions of these packages have support for bonding, while older +versions do not. + + We will first describe the options for configuring bonding for +distros using versions of initscripts, sysconfig and interfaces with full +or partial support for bonding, then provide information on enabling +bonding without support from the network initialization scripts (i.e., +older versions of initscripts or sysconfig). + + If you're unsure whether your distro uses sysconfig, +initscripts or interfaces, or don't know if it's new enough, have no fear. +Determining this is fairly straightforward. + + First, look for a file called interfaces in /etc/network directory. +If this file is present in your system, then your system use interfaces. See +Configuration with Interfaces Support. + + Else, issue the command: + +$ rpm -qf /sbin/ifup + + It will respond with a line of text starting with either +"initscripts" or "sysconfig," followed by some numbers. This is the +package that provides your network initialization scripts. + + Next, to determine if your installation supports bonding, +issue the command: + +$ grep ifenslave /sbin/ifup + + If this returns any matches, then your initscripts or +sysconfig has support for bonding. + +3.1 Configuration with Sysconfig Support +---------------------------------------- + + This section applies to distros using a version of sysconfig +with bonding support, for example, SuSE Linux Enterprise Server 9. + + SuSE SLES 9's networking configuration system does support +bonding, however, at this writing, the YaST system configuration +front end does not provide any means to work with bonding devices. +Bonding devices can be managed by hand, however, as follows. + + First, if they have not already been configured, configure the +slave devices. On SLES 9, this is most easily done by running the +yast2 sysconfig configuration utility. The goal is for to create an +ifcfg-id file for each slave device. The simplest way to accomplish +this is to configure the devices for DHCP (this is only to get the +file ifcfg-id file created; see below for some issues with DHCP). The +name of the configuration file for each device will be of the form: + +ifcfg-id-xx:xx:xx:xx:xx:xx + + Where the "xx" portion will be replaced with the digits from +the device's permanent MAC address. + + Once the set of ifcfg-id-xx:xx:xx:xx:xx:xx files has been +created, it is necessary to edit the configuration files for the slave +devices (the MAC addresses correspond to those of the slave devices). +Before editing, the file will contain multiple lines, and will look +something like this: + +BOOTPROTO='dhcp' +STARTMODE='on' +USERCTL='no' +UNIQUE='XNzu.WeZGOGF+4wE' +_nm_name='bus-pci-0001:61:01.0' + + Change the BOOTPROTO and STARTMODE lines to the following: + +BOOTPROTO='none' +STARTMODE='off' + + Do not alter the UNIQUE or _nm_name lines. Remove any other +lines (USERCTL, etc). + + Once the ifcfg-id-xx:xx:xx:xx:xx:xx files have been modified, +it's time to create the configuration file for the bonding device +itself. This file is named ifcfg-bondX, where X is the number of the +bonding device to create, starting at 0. The first such file is +ifcfg-bond0, the second is ifcfg-bond1, and so on. The sysconfig +network configuration system will correctly start multiple instances +of bonding. + + The contents of the ifcfg-bondX file is as follows: + +BOOTPROTO="static" +BROADCAST="10.0.2.255" +IPADDR="10.0.2.10" +NETMASK="255.255.0.0" +NETWORK="10.0.2.0" +REMOTE_IPADDR="" +STARTMODE="onboot" +BONDING_MASTER="yes" +BONDING_MODULE_OPTS="mode=active-backup miimon=100" +BONDING_SLAVE0="eth0" +BONDING_SLAVE1="bus-pci-0000:06:08.1" + + Replace the sample BROADCAST, IPADDR, NETMASK and NETWORK +values with the appropriate values for your network. + + The STARTMODE specifies when the device is brought online. +The possible values are: + + onboot: The device is started at boot time. If you're not + sure, this is probably what you want. + + manual: The device is started only when ifup is called + manually. Bonding devices may be configured this + way if you do not wish them to start automatically + at boot for some reason. + + hotplug: The device is started by a hotplug event. This is not + a valid choice for a bonding device. + + off or ignore: The device configuration is ignored. + + The line BONDING_MASTER='yes' indicates that the device is a +bonding master device. The only useful value is "yes." + + The contents of BONDING_MODULE_OPTS are supplied to the +instance of the bonding module for this device. Specify the options +for the bonding mode, link monitoring, and so on here. Do not include +the max_bonds bonding parameter; this will confuse the configuration +system if you have multiple bonding devices. + + Finally, supply one BONDING_SLAVEn="slave device" for each +slave. where "n" is an increasing value, one for each slave. The +"slave device" is either an interface name, e.g., "eth0", or a device +specifier for the network device. The interface name is easier to +find, but the ethN names are subject to change at boot time if, e.g., +a device early in the sequence has failed. The device specifiers +(bus-pci-0000:06:08.1 in the example above) specify the physical +network device, and will not change unless the device's bus location +changes (for example, it is moved from one PCI slot to another). The +example above uses one of each type for demonstration purposes; most +configurations will choose one or the other for all slave devices. + + When all configuration files have been modified or created, +networking must be restarted for the configuration changes to take +effect. This can be accomplished via the following: + +# /etc/init.d/network restart + + Note that the network control script (/sbin/ifdown) will +remove the bonding module as part of the network shutdown processing, +so it is not necessary to remove the module by hand if, e.g., the +module parameters have changed. + + Also, at this writing, YaST/YaST2 will not manage bonding +devices (they do not show bonding interfaces on its list of network +devices). It is necessary to edit the configuration file by hand to +change the bonding configuration. + + Additional general options and details of the ifcfg file +format can be found in an example ifcfg template file: + +/etc/sysconfig/network/ifcfg.template + + Note that the template does not document the various BONDING_ +settings described above, but does describe many of the other options. + +3.1.1 Using DHCP with Sysconfig +------------------------------- + + Under sysconfig, configuring a device with BOOTPROTO='dhcp' +will cause it to query DHCP for its IP address information. At this +writing, this does not function for bonding devices; the scripts +attempt to obtain the device address from DHCP prior to adding any of +the slave devices. Without active slaves, the DHCP requests are not +sent to the network. + +3.1.2 Configuring Multiple Bonds with Sysconfig +----------------------------------------------- + + The sysconfig network initialization system is capable of +handling multiple bonding devices. All that is necessary is for each +bonding instance to have an appropriately configured ifcfg-bondX file +(as described above). Do not specify the "max_bonds" parameter to any +instance of bonding, as this will confuse sysconfig. If you require +multiple bonding devices with identical parameters, create multiple +ifcfg-bondX files. + + Because the sysconfig scripts supply the bonding module +options in the ifcfg-bondX file, it is not necessary to add them to +the system /etc/modules.d/*.conf configuration files. + +3.2 Configuration with Initscripts Support +------------------------------------------ + + This section applies to distros using a recent version of +initscripts with bonding support, for example, Red Hat Enterprise Linux +version 3 or later, Fedora, etc. On these systems, the network +initialization scripts have knowledge of bonding, and can be configured to +control bonding devices. Note that older versions of the initscripts +package have lower levels of support for bonding; this will be noted where +applicable. + + These distros will not automatically load the network adapter +driver unless the ethX device is configured with an IP address. +Because of this constraint, users must manually configure a +network-script file for all physical adapters that will be members of +a bondX link. Network script files are located in the directory: + +/etc/sysconfig/network-scripts + + The file name must be prefixed with "ifcfg-eth" and suffixed +with the adapter's physical adapter number. For example, the script +for eth0 would be named /etc/sysconfig/network-scripts/ifcfg-eth0. +Place the following text in the file: + +DEVICE=eth0 +USERCTL=no +ONBOOT=yes +MASTER=bond0 +SLAVE=yes +BOOTPROTO=none + + The DEVICE= line will be different for every ethX device and +must correspond with the name of the file, i.e., ifcfg-eth1 must have +a device line of DEVICE=eth1. The setting of the MASTER= line will +also depend on the final bonding interface name chosen for your bond. +As with other network devices, these typically start at 0, and go up +one for each device, i.e., the first bonding instance is bond0, the +second is bond1, and so on. + + Next, create a bond network script. The file name for this +script will be /etc/sysconfig/network-scripts/ifcfg-bondX where X is +the number of the bond. For bond0 the file is named "ifcfg-bond0", +for bond1 it is named "ifcfg-bond1", and so on. Within that file, +place the following text: + +DEVICE=bond0 +IPADDR=192.168.1.1 +NETMASK=255.255.255.0 +NETWORK=192.168.1.0 +BROADCAST=192.168.1.255 +ONBOOT=yes +BOOTPROTO=none +USERCTL=no + + Be sure to change the networking specific lines (IPADDR, +NETMASK, NETWORK and BROADCAST) to match your network configuration. + + For later versions of initscripts, such as that found with Fedora +7 (or later) and Red Hat Enterprise Linux version 5 (or later), it is possible, +and, indeed, preferable, to specify the bonding options in the ifcfg-bond0 +file, e.g. a line of the format: + +BONDING_OPTS="mode=active-backup arp_interval=60 arp_ip_target=192.168.1.254" + + will configure the bond with the specified options. The options +specified in BONDING_OPTS are identical to the bonding module parameters +except for the arp_ip_target field when using versions of initscripts older +than and 8.57 (Fedora 8) and 8.45.19 (Red Hat Enterprise Linux 5.2). When +using older versions each target should be included as a separate option and +should be preceded by a '+' to indicate it should be added to the list of +queried targets, e.g., + + arp_ip_target=+192.168.1.1 arp_ip_target=+192.168.1.2 + + is the proper syntax to specify multiple targets. When specifying +options via BONDING_OPTS, it is not necessary to edit /etc/modprobe.d/*.conf. + + For even older versions of initscripts that do not support +BONDING_OPTS, it is necessary to edit /etc/modprobe.d/*.conf, depending upon +your distro) to load the bonding module with your desired options when the +bond0 interface is brought up. The following lines in /etc/modprobe.d/*.conf +will load the bonding module, and select its options: + +alias bond0 bonding +options bond0 mode=balance-alb miimon=100 + + Replace the sample parameters with the appropriate set of +options for your configuration. + + Finally run "/etc/rc.d/init.d/network restart" as root. This +will restart the networking subsystem and your bond link should be now +up and running. + +3.2.1 Using DHCP with Initscripts +--------------------------------- + + Recent versions of initscripts (the versions supplied with Fedora +Core 3 and Red Hat Enterprise Linux 4, or later versions, are reported to +work) have support for assigning IP information to bonding devices via +DHCP. + + To configure bonding for DHCP, configure it as described +above, except replace the line "BOOTPROTO=none" with "BOOTPROTO=dhcp" +and add a line consisting of "TYPE=Bonding". Note that the TYPE value +is case sensitive. + +3.2.2 Configuring Multiple Bonds with Initscripts +------------------------------------------------- + + Initscripts packages that are included with Fedora 7 and Red Hat +Enterprise Linux 5 support multiple bonding interfaces by simply +specifying the appropriate BONDING_OPTS= in ifcfg-bondX where X is the +number of the bond. This support requires sysfs support in the kernel, +and a bonding driver of version 3.0.0 or later. Other configurations may +not support this method for specifying multiple bonding interfaces; for +those instances, see the "Configuring Multiple Bonds Manually" section, +below. + +3.3 Configuring Bonding Manually with Ifenslave +----------------------------------------------- + + This section applies to distros whose network initialization +scripts (the sysconfig or initscripts package) do not have specific +knowledge of bonding. One such distro is SuSE Linux Enterprise Server +version 8. + + The general method for these systems is to place the bonding +module parameters into a config file in /etc/modprobe.d/ (as +appropriate for the installed distro), then add modprobe and/or +ifenslave commands to the system's global init script. The name of +the global init script differs; for sysconfig, it is +/etc/init.d/boot.local and for initscripts it is /etc/rc.d/rc.local. + + For example, if you wanted to make a simple bond of two e100 +devices (presumed to be eth0 and eth1), and have it persist across +reboots, edit the appropriate file (/etc/init.d/boot.local or +/etc/rc.d/rc.local), and add the following: + +modprobe bonding mode=balance-alb miimon=100 +modprobe e100 +ifconfig bond0 192.168.1.1 netmask 255.255.255.0 up +ifenslave bond0 eth0 +ifenslave bond0 eth1 + + Replace the example bonding module parameters and bond0 +network configuration (IP address, netmask, etc) with the appropriate +values for your configuration. + + Unfortunately, this method will not provide support for the +ifup and ifdown scripts on the bond devices. To reload the bonding +configuration, it is necessary to run the initialization script, e.g., + +# /etc/init.d/boot.local + + or + +# /etc/rc.d/rc.local + + It may be desirable in such a case to create a separate script +which only initializes the bonding configuration, then call that +separate script from within boot.local. This allows for bonding to be +enabled without re-running the entire global init script. + + To shut down the bonding devices, it is necessary to first +mark the bonding device itself as being down, then remove the +appropriate device driver modules. For our example above, you can do +the following: + +# ifconfig bond0 down +# rmmod bonding +# rmmod e100 + + Again, for convenience, it may be desirable to create a script +with these commands. + + +3.3.1 Configuring Multiple Bonds Manually +----------------------------------------- + + This section contains information on configuring multiple +bonding devices with differing options for those systems whose network +initialization scripts lack support for configuring multiple bonds. + + If you require multiple bonding devices, but all with the same +options, you may wish to use the "max_bonds" module parameter, +documented above. + + To create multiple bonding devices with differing options, it is +preferable to use bonding parameters exported by sysfs, documented in the +section below. + + For versions of bonding without sysfs support, the only means to +provide multiple instances of bonding with differing options is to load +the bonding driver multiple times. Note that current versions of the +sysconfig network initialization scripts handle this automatically; if +your distro uses these scripts, no special action is needed. See the +section Configuring Bonding Devices, above, if you're not sure about your +network initialization scripts. + + To load multiple instances of the module, it is necessary to +specify a different name for each instance (the module loading system +requires that every loaded module, even multiple instances of the same +module, have a unique name). This is accomplished by supplying multiple +sets of bonding options in /etc/modprobe.d/*.conf, for example: + +alias bond0 bonding +options bond0 -o bond0 mode=balance-rr miimon=100 + +alias bond1 bonding +options bond1 -o bond1 mode=balance-alb miimon=50 + + will load the bonding module two times. The first instance is +named "bond0" and creates the bond0 device in balance-rr mode with an +miimon of 100. The second instance is named "bond1" and creates the +bond1 device in balance-alb mode with an miimon of 50. + + In some circumstances (typically with older distributions), +the above does not work, and the second bonding instance never sees +its options. In that case, the second options line can be substituted +as follows: + +install bond1 /sbin/modprobe --ignore-install bonding -o bond1 \ + mode=balance-alb miimon=50 + + This may be repeated any number of times, specifying a new and +unique name in place of bond1 for each subsequent instance. + + It has been observed that some Red Hat supplied kernels are unable +to rename modules at load time (the "-o bond1" part). Attempts to pass +that option to modprobe will produce an "Operation not permitted" error. +This has been reported on some Fedora Core kernels, and has been seen on +RHEL 4 as well. On kernels exhibiting this problem, it will be impossible +to configure multiple bonds with differing parameters (as they are older +kernels, and also lack sysfs support). + +3.4 Configuring Bonding Manually via Sysfs +------------------------------------------ + + Starting with version 3.0.0, Channel Bonding may be configured +via the sysfs interface. This interface allows dynamic configuration +of all bonds in the system without unloading the module. It also +allows for adding and removing bonds at runtime. Ifenslave is no +longer required, though it is still supported. + + Use of the sysfs interface allows you to use multiple bonds +with different configurations without having to reload the module. +It also allows you to use multiple, differently configured bonds when +bonding is compiled into the kernel. + + You must have the sysfs filesystem mounted to configure +bonding this way. The examples in this document assume that you +are using the standard mount point for sysfs, e.g. /sys. If your +sysfs filesystem is mounted elsewhere, you will need to adjust the +example paths accordingly. + +Creating and Destroying Bonds +----------------------------- +To add a new bond foo: +# echo +foo > /sys/class/net/bonding_masters + +To remove an existing bond bar: +# echo -bar > /sys/class/net/bonding_masters + +To show all existing bonds: +# cat /sys/class/net/bonding_masters + +NOTE: due to 4K size limitation of sysfs files, this list may be +truncated if you have more than a few hundred bonds. This is unlikely +to occur under normal operating conditions. + +Adding and Removing Slaves +-------------------------- + Interfaces may be enslaved to a bond using the file +/sys/class/net//bonding/slaves. The semantics for this file +are the same as for the bonding_masters file. + +To enslave interface eth0 to bond bond0: +# ifconfig bond0 up +# echo +eth0 > /sys/class/net/bond0/bonding/slaves + +To free slave eth0 from bond bond0: +# echo -eth0 > /sys/class/net/bond0/bonding/slaves + + When an interface is enslaved to a bond, symlinks between the +two are created in the sysfs filesystem. In this case, you would get +/sys/class/net/bond0/slave_eth0 pointing to /sys/class/net/eth0, and +/sys/class/net/eth0/master pointing to /sys/class/net/bond0. + + This means that you can tell quickly whether or not an +interface is enslaved by looking for the master symlink. Thus: +# echo -eth0 > /sys/class/net/eth0/master/bonding/slaves +will free eth0 from whatever bond it is enslaved to, regardless of +the name of the bond interface. + +Changing a Bond's Configuration +------------------------------- + Each bond may be configured individually by manipulating the +files located in /sys/class/net//bonding + + The names of these files correspond directly with the command- +line parameters described elsewhere in this file, and, with the +exception of arp_ip_target, they accept the same values. To see the +current setting, simply cat the appropriate file. + + A few examples will be given here; for specific usage +guidelines for each parameter, see the appropriate section in this +document. + +To configure bond0 for balance-alb mode: +# ifconfig bond0 down +# echo 6 > /sys/class/net/bond0/bonding/mode + - or - +# echo balance-alb > /sys/class/net/bond0/bonding/mode + NOTE: The bond interface must be down before the mode can be +changed. + +To enable MII monitoring on bond0 with a 1 second interval: +# echo 1000 > /sys/class/net/bond0/bonding/miimon + NOTE: If ARP monitoring is enabled, it will disabled when MII +monitoring is enabled, and vice-versa. + +To add ARP targets: +# echo +192.168.0.100 > /sys/class/net/bond0/bonding/arp_ip_target +# echo +192.168.0.101 > /sys/class/net/bond0/bonding/arp_ip_target + NOTE: up to 16 target addresses may be specified. + +To remove an ARP target: +# echo -192.168.0.100 > /sys/class/net/bond0/bonding/arp_ip_target + +Example Configuration +--------------------- + We begin with the same example that is shown in section 3.3, +executed with sysfs, and without using ifenslave. + + To make a simple bond of two e100 devices (presumed to be eth0 +and eth1), and have it persist across reboots, edit the appropriate +file (/etc/init.d/boot.local or /etc/rc.d/rc.local), and add the +following: + +modprobe bonding +modprobe e100 +echo balance-alb > /sys/class/net/bond0/bonding/mode +ifconfig bond0 192.168.1.1 netmask 255.255.255.0 up +echo 100 > /sys/class/net/bond0/bonding/miimon +echo +eth0 > /sys/class/net/bond0/bonding/slaves +echo +eth1 > /sys/class/net/bond0/bonding/slaves + + To add a second bond, with two e1000 interfaces in +active-backup mode, using ARP monitoring, add the following lines to +your init script: + +modprobe e1000 +echo +bond1 > /sys/class/net/bonding_masters +echo active-backup > /sys/class/net/bond1/bonding/mode +ifconfig bond1 192.168.2.1 netmask 255.255.255.0 up +echo +192.168.2.100 /sys/class/net/bond1/bonding/arp_ip_target +echo 2000 > /sys/class/net/bond1/bonding/arp_interval +echo +eth2 > /sys/class/net/bond1/bonding/slaves +echo +eth3 > /sys/class/net/bond1/bonding/slaves + +3.5 Configuration with Interfaces Support +----------------------------------------- + + This section applies to distros which use /etc/network/interfaces file +to describe network interface configuration, most notably Debian and it's +derivatives. + + The ifup and ifdown commands on Debian don't support bonding out of +the box. The ifenslave-2.6 package should be installed to provide bonding +support. Once installed, this package will provide bond-* options to be used +into /etc/network/interfaces. + + Note that ifenslave-2.6 package will load the bonding module and use +the ifenslave command when appropriate. + +Example Configurations +---------------------- + +In /etc/network/interfaces, the following stanza will configure bond0, in +active-backup mode, with eth0 and eth1 as slaves. + +auto bond0 +iface bond0 inet dhcp + bond-slaves eth0 eth1 + bond-mode active-backup + bond-miimon 100 + bond-primary eth0 eth1 + +If the above configuration doesn't work, you might have a system using +upstart for system startup. This is most notably true for recent +Ubuntu versions. The following stanza in /etc/network/interfaces will +produce the same result on those systems. + +auto bond0 +iface bond0 inet dhcp + bond-slaves none + bond-mode active-backup + bond-miimon 100 + +auto eth0 +iface eth0 inet manual + bond-master bond0 + bond-primary eth0 eth1 + +auto eth1 +iface eth1 inet manual + bond-master bond0 + bond-primary eth0 eth1 + +For a full list of bond-* supported options in /etc/network/interfaces and some +more advanced examples tailored to you particular distros, see the files in +/usr/share/doc/ifenslave-2.6. + +3.6 Overriding Configuration for Special Cases +---------------------------------------------- + +When using the bonding driver, the physical port which transmits a frame is +typically selected by the bonding driver, and is not relevant to the user or +system administrator. The output port is simply selected using the policies of +the selected bonding mode. On occasion however, it is helpful to direct certain +classes of traffic to certain physical interfaces on output to implement +slightly more complex policies. For example, to reach a web server over a +bonded interface in which eth0 connects to a private network, while eth1 +connects via a public network, it may be desirous to bias the bond to send said +traffic over eth0 first, using eth1 only as a fall back, while all other traffic +can safely be sent over either interface. Such configurations may be achieved +using the traffic control utilities inherent in linux. + +By default the bonding driver is multiqueue aware and 16 queues are created +when the driver initializes (see Documentation/networking/multiqueue.txt +for details). If more or less queues are desired the module parameter +tx_queues can be used to change this value. There is no sysfs parameter +available as the allocation is done at module init time. + +The output of the file /proc/net/bonding/bondX has changed so the output Queue +ID is now printed for each slave: + +Bonding Mode: fault-tolerance (active-backup) +Primary Slave: None +Currently Active Slave: eth0 +MII Status: up +MII Polling Interval (ms): 0 +Up Delay (ms): 0 +Down Delay (ms): 0 + +Slave Interface: eth0 +MII Status: up +Link Failure Count: 0 +Permanent HW addr: 00:1a:a0:12:8f:cb +Slave queue ID: 0 + +Slave Interface: eth1 +MII Status: up +Link Failure Count: 0 +Permanent HW addr: 00:1a:a0:12:8f:cc +Slave queue ID: 2 + +The queue_id for a slave can be set using the command: + +# echo "eth1:2" > /sys/class/net/bond0/bonding/queue_id + +Any interface that needs a queue_id set should set it with multiple calls +like the one above until proper priorities are set for all interfaces. On +distributions that allow configuration via initscripts, multiple 'queue_id' +arguments can be added to BONDING_OPTS to set all needed slave queues. + +These queue id's can be used in conjunction with the tc utility to configure +a multiqueue qdisc and filters to bias certain traffic to transmit on certain +slave devices. For instance, say we wanted, in the above configuration to +force all traffic bound to 192.168.1.100 to use eth1 in the bond as its output +device. The following commands would accomplish this: + +# tc qdisc add dev bond0 handle 1 root multiq + +# tc filter add dev bond0 protocol ip parent 1: prio 1 u32 match ip dst \ + 192.168.1.100 action skbedit queue_mapping 2 + +These commands tell the kernel to attach a multiqueue queue discipline to the +bond0 interface and filter traffic enqueued to it, such that packets with a dst +ip of 192.168.1.100 have their output queue mapping value overwritten to 2. +This value is then passed into the driver, causing the normal output path +selection policy to be overridden, selecting instead qid 2, which maps to eth1. + +Note that qid values begin at 1. Qid 0 is reserved to initiate to the driver +that normal output policy selection should take place. One benefit to simply +leaving the qid for a slave to 0 is the multiqueue awareness in the bonding +driver that is now present. This awareness allows tc filters to be placed on +slave devices as well as bond devices and the bonding driver will simply act as +a pass-through for selecting output queues on the slave device rather than +output port selection. + +This feature first appeared in bonding driver version 3.7.0 and support for +output slave selection was limited to round-robin and active-backup modes. + +4 Querying Bonding Configuration +================================= + +4.1 Bonding Configuration +------------------------- + + Each bonding device has a read-only file residing in the +/proc/net/bonding directory. The file contents include information +about the bonding configuration, options and state of each slave. + + For example, the contents of /proc/net/bonding/bond0 after the +driver is loaded with parameters of mode=0 and miimon=1000 is +generally as follows: + + Ethernet Channel Bonding Driver: 2.6.1 (October 29, 2004) + Bonding Mode: load balancing (round-robin) + Currently Active Slave: eth0 + MII Status: up + MII Polling Interval (ms): 1000 + Up Delay (ms): 0 + Down Delay (ms): 0 + + Slave Interface: eth1 + MII Status: up + Link Failure Count: 1 + + Slave Interface: eth0 + MII Status: up + Link Failure Count: 1 + + The precise format and contents will change depending upon the +bonding configuration, state, and version of the bonding driver. + +4.2 Network configuration +------------------------- + + The network configuration can be inspected using the ifconfig +command. Bonding devices will have the MASTER flag set; Bonding slave +devices will have the SLAVE flag set. The ifconfig output does not +contain information on which slaves are associated with which masters. + + In the example below, the bond0 interface is the master +(MASTER) while eth0 and eth1 are slaves (SLAVE). Notice all slaves of +bond0 have the same MAC address (HWaddr) as bond0 for all modes except +TLB and ALB that require a unique MAC address for each slave. + +# /sbin/ifconfig +bond0 Link encap:Ethernet HWaddr 00:C0:F0:1F:37:B4 + inet addr:XXX.XXX.XXX.YYY Bcast:XXX.XXX.XXX.255 Mask:255.255.252.0 + UP BROADCAST RUNNING MASTER MULTICAST MTU:1500 Metric:1 + RX packets:7224794 errors:0 dropped:0 overruns:0 frame:0 + TX packets:3286647 errors:1 dropped:0 overruns:1 carrier:0 + collisions:0 txqueuelen:0 + +eth0 Link encap:Ethernet HWaddr 00:C0:F0:1F:37:B4 + UP BROADCAST RUNNING SLAVE MULTICAST MTU:1500 Metric:1 + RX packets:3573025 errors:0 dropped:0 overruns:0 frame:0 + TX packets:1643167 errors:1 dropped:0 overruns:1 carrier:0 + collisions:0 txqueuelen:100 + Interrupt:10 Base address:0x1080 + +eth1 Link encap:Ethernet HWaddr 00:C0:F0:1F:37:B4 + UP BROADCAST RUNNING SLAVE MULTICAST MTU:1500 Metric:1 + RX packets:3651769 errors:0 dropped:0 overruns:0 frame:0 + TX packets:1643480 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:100 + Interrupt:9 Base address:0x1400 + +5. Switch Configuration +======================= + + For this section, "switch" refers to whatever system the +bonded devices are directly connected to (i.e., where the other end of +the cable plugs into). This may be an actual dedicated switch device, +or it may be another regular system (e.g., another computer running +Linux), + + The active-backup, balance-tlb and balance-alb modes do not +require any specific configuration of the switch. + + The 802.3ad mode requires that the switch have the appropriate +ports configured as an 802.3ad aggregation. The precise method used +to configure this varies from switch to switch, but, for example, a +Cisco 3550 series switch requires that the appropriate ports first be +grouped together in a single etherchannel instance, then that +etherchannel is set to mode "lacp" to enable 802.3ad (instead of +standard EtherChannel). + + The balance-rr, balance-xor and broadcast modes generally +require that the switch have the appropriate ports grouped together. +The nomenclature for such a group differs between switches, it may be +called an "etherchannel" (as in the Cisco example, above), a "trunk +group" or some other similar variation. For these modes, each switch +will also have its own configuration options for the switch's transmit +policy to the bond. Typical choices include XOR of either the MAC or +IP addresses. The transmit policy of the two peers does not need to +match. For these three modes, the bonding mode really selects a +transmit policy for an EtherChannel group; all three will interoperate +with another EtherChannel group. + + +6. 802.1q VLAN Support +====================== + + It is possible to configure VLAN devices over a bond interface +using the 8021q driver. However, only packets coming from the 8021q +driver and passing through bonding will be tagged by default. Self +generated packets, for example, bonding's learning packets or ARP +packets generated by either ALB mode or the ARP monitor mechanism, are +tagged internally by bonding itself. As a result, bonding must +"learn" the VLAN IDs configured above it, and use those IDs to tag +self generated packets. + + For reasons of simplicity, and to support the use of adapters +that can do VLAN hardware acceleration offloading, the bonding +interface declares itself as fully hardware offloading capable, it gets +the add_vid/kill_vid notifications to gather the necessary +information, and it propagates those actions to the slaves. In case +of mixed adapter types, hardware accelerated tagged packets that +should go through an adapter that is not offloading capable are +"un-accelerated" by the bonding driver so the VLAN tag sits in the +regular location. + + VLAN interfaces *must* be added on top of a bonding interface +only after enslaving at least one slave. The bonding interface has a +hardware address of 00:00:00:00:00:00 until the first slave is added. +If the VLAN interface is created prior to the first enslavement, it +would pick up the all-zeroes hardware address. Once the first slave +is attached to the bond, the bond device itself will pick up the +slave's hardware address, which is then available for the VLAN device. + + Also, be aware that a similar problem can occur if all slaves +are released from a bond that still has one or more VLAN interfaces on +top of it. When a new slave is added, the bonding interface will +obtain its hardware address from the first slave, which might not +match the hardware address of the VLAN interfaces (which was +ultimately copied from an earlier slave). + + There are two methods to insure that the VLAN device operates +with the correct hardware address if all slaves are removed from a +bond interface: + + 1. Remove all VLAN interfaces then recreate them + + 2. Set the bonding interface's hardware address so that it +matches the hardware address of the VLAN interfaces. + + Note that changing a VLAN interface's HW address would set the +underlying device -- i.e. the bonding interface -- to promiscuous +mode, which might not be what you want. + + +7. Link Monitoring +================== + + The bonding driver at present supports two schemes for +monitoring a slave device's link state: the ARP monitor and the MII +monitor. + + At the present time, due to implementation restrictions in the +bonding driver itself, it is not possible to enable both ARP and MII +monitoring simultaneously. + +7.1 ARP Monitor Operation +------------------------- + + The ARP monitor operates as its name suggests: it sends ARP +queries to one or more designated peer systems on the network, and +uses the response as an indication that the link is operating. This +gives some assurance that traffic is actually flowing to and from one +or more peers on the local network. + + The ARP monitor relies on the device driver itself to verify +that traffic is flowing. In particular, the driver must keep up to +date the last receive time, dev->last_rx, and transmit start time, +dev->trans_start. If these are not updated by the driver, then the +ARP monitor will immediately fail any slaves using that driver, and +those slaves will stay down. If networking monitoring (tcpdump, etc) +shows the ARP requests and replies on the network, then it may be that +your device driver is not updating last_rx and trans_start. + +7.2 Configuring Multiple ARP Targets +------------------------------------ + + While ARP monitoring can be done with just one target, it can +be useful in a High Availability setup to have several targets to +monitor. In the case of just one target, the target itself may go +down or have a problem making it unresponsive to ARP requests. Having +an additional target (or several) increases the reliability of the ARP +monitoring. + + Multiple ARP targets must be separated by commas as follows: + +# example options for ARP monitoring with three targets +alias bond0 bonding +options bond0 arp_interval=60 arp_ip_target=192.168.0.1,192.168.0.3,192.168.0.9 + + For just a single target the options would resemble: + +# example options for ARP monitoring with one target +alias bond0 bonding +options bond0 arp_interval=60 arp_ip_target=192.168.0.100 + + +7.3 MII Monitor Operation +------------------------- + + The MII monitor monitors only the carrier state of the local +network interface. It accomplishes this in one of three ways: by +depending upon the device driver to maintain its carrier state, by +querying the device's MII registers, or by making an ethtool query to +the device. + + If the use_carrier module parameter is 1 (the default value), +then the MII monitor will rely on the driver for carrier state +information (via the netif_carrier subsystem). As explained in the +use_carrier parameter information, above, if the MII monitor fails to +detect carrier loss on the device (e.g., when the cable is physically +disconnected), it may be that the driver does not support +netif_carrier. + + If use_carrier is 0, then the MII monitor will first query the +device's (via ioctl) MII registers and check the link state. If that +request fails (not just that it returns carrier down), then the MII +monitor will make an ethtool ETHOOL_GLINK request to attempt to obtain +the same information. If both methods fail (i.e., the driver either +does not support or had some error in processing both the MII register +and ethtool requests), then the MII monitor will assume the link is +up. + +8. Potential Sources of Trouble +=============================== + +8.1 Adventures in Routing +------------------------- + + When bonding is configured, it is important that the slave +devices not have routes that supersede routes of the master (or, +generally, not have routes at all). For example, suppose the bonding +device bond0 has two slaves, eth0 and eth1, and the routing table is +as follows: + +Kernel IP routing table +Destination Gateway Genmask Flags MSS Window irtt Iface +10.0.0.0 0.0.0.0 255.255.0.0 U 40 0 0 eth0 +10.0.0.0 0.0.0.0 255.255.0.0 U 40 0 0 eth1 +10.0.0.0 0.0.0.0 255.255.0.0 U 40 0 0 bond0 +127.0.0.0 0.0.0.0 255.0.0.0 U 40 0 0 lo + + This routing configuration will likely still update the +receive/transmit times in the driver (needed by the ARP monitor), but +may bypass the bonding driver (because outgoing traffic to, in this +case, another host on network 10 would use eth0 or eth1 before bond0). + + The ARP monitor (and ARP itself) may become confused by this +configuration, because ARP requests (generated by the ARP monitor) +will be sent on one interface (bond0), but the corresponding reply +will arrive on a different interface (eth0). This reply looks to ARP +as an unsolicited ARP reply (because ARP matches replies on an +interface basis), and is discarded. The MII monitor is not affected +by the state of the routing table. + + The solution here is simply to insure that slaves do not have +routes of their own, and if for some reason they must, those routes do +not supersede routes of their master. This should generally be the +case, but unusual configurations or errant manual or automatic static +route additions may cause trouble. + +8.2 Ethernet Device Renaming +---------------------------- + + On systems with network configuration scripts that do not +associate physical devices directly with network interface names (so +that the same physical device always has the same "ethX" name), it may +be necessary to add some special logic to config files in +/etc/modprobe.d/. + + For example, given a modules.conf containing the following: + +alias bond0 bonding +options bond0 mode=some-mode miimon=50 +alias eth0 tg3 +alias eth1 tg3 +alias eth2 e1000 +alias eth3 e1000 + + If neither eth0 and eth1 are slaves to bond0, then when the +bond0 interface comes up, the devices may end up reordered. This +happens because bonding is loaded first, then its slave device's +drivers are loaded next. Since no other drivers have been loaded, +when the e1000 driver loads, it will receive eth0 and eth1 for its +devices, but the bonding configuration tries to enslave eth2 and eth3 +(which may later be assigned to the tg3 devices). + + Adding the following: + +add above bonding e1000 tg3 + + causes modprobe to load e1000 then tg3, in that order, when +bonding is loaded. This command is fully documented in the +modules.conf manual page. + + On systems utilizing modprobe an equivalent problem can occur. +In this case, the following can be added to config files in +/etc/modprobe.d/ as: + +softdep bonding pre: tg3 e1000 + + This will load tg3 and e1000 modules before loading the bonding one. +Full documentation on this can be found in the modprobe.d and modprobe +manual pages. + +8.3. Painfully Slow Or No Failed Link Detection By Miimon +--------------------------------------------------------- + + By default, bonding enables the use_carrier option, which +instructs bonding to trust the driver to maintain carrier state. + + As discussed in the options section, above, some drivers do +not support the netif_carrier_on/_off link state tracking system. +With use_carrier enabled, bonding will always see these links as up, +regardless of their actual state. + + Additionally, other drivers do support netif_carrier, but do +not maintain it in real time, e.g., only polling the link state at +some fixed interval. In this case, miimon will detect failures, but +only after some long period of time has expired. If it appears that +miimon is very slow in detecting link failures, try specifying +use_carrier=0 to see if that improves the failure detection time. If +it does, then it may be that the driver checks the carrier state at a +fixed interval, but does not cache the MII register values (so the +use_carrier=0 method of querying the registers directly works). If +use_carrier=0 does not improve the failover, then the driver may cache +the registers, or the problem may be elsewhere. + + Also, remember that miimon only checks for the device's +carrier state. It has no way to determine the state of devices on or +beyond other ports of a switch, or if a switch is refusing to pass +traffic while still maintaining carrier on. + +9. SNMP agents +=============== + + If running SNMP agents, the bonding driver should be loaded +before any network drivers participating in a bond. This requirement +is due to the interface index (ipAdEntIfIndex) being associated to +the first interface found with a given IP address. That is, there is +only one ipAdEntIfIndex for each IP address. For example, if eth0 and +eth1 are slaves of bond0 and the driver for eth0 is loaded before the +bonding driver, the interface for the IP address will be associated +with the eth0 interface. This configuration is shown below, the IP +address 192.168.1.1 has an interface index of 2 which indexes to eth0 +in the ifDescr table (ifDescr.2). + + interfaces.ifTable.ifEntry.ifDescr.1 = lo + interfaces.ifTable.ifEntry.ifDescr.2 = eth0 + interfaces.ifTable.ifEntry.ifDescr.3 = eth1 + interfaces.ifTable.ifEntry.ifDescr.4 = eth2 + interfaces.ifTable.ifEntry.ifDescr.5 = eth3 + interfaces.ifTable.ifEntry.ifDescr.6 = bond0 + ip.ipAddrTable.ipAddrEntry.ipAdEntIfIndex.10.10.10.10 = 5 + ip.ipAddrTable.ipAddrEntry.ipAdEntIfIndex.192.168.1.1 = 2 + ip.ipAddrTable.ipAddrEntry.ipAdEntIfIndex.10.74.20.94 = 4 + ip.ipAddrTable.ipAddrEntry.ipAdEntIfIndex.127.0.0.1 = 1 + + This problem is avoided by loading the bonding driver before +any network drivers participating in a bond. Below is an example of +loading the bonding driver first, the IP address 192.168.1.1 is +correctly associated with ifDescr.2. + + interfaces.ifTable.ifEntry.ifDescr.1 = lo + interfaces.ifTable.ifEntry.ifDescr.2 = bond0 + interfaces.ifTable.ifEntry.ifDescr.3 = eth0 + interfaces.ifTable.ifEntry.ifDescr.4 = eth1 + interfaces.ifTable.ifEntry.ifDescr.5 = eth2 + interfaces.ifTable.ifEntry.ifDescr.6 = eth3 + ip.ipAddrTable.ipAddrEntry.ipAdEntIfIndex.10.10.10.10 = 6 + ip.ipAddrTable.ipAddrEntry.ipAdEntIfIndex.192.168.1.1 = 2 + ip.ipAddrTable.ipAddrEntry.ipAdEntIfIndex.10.74.20.94 = 5 + ip.ipAddrTable.ipAddrEntry.ipAdEntIfIndex.127.0.0.1 = 1 + + While some distributions may not report the interface name in +ifDescr, the association between the IP address and IfIndex remains +and SNMP functions such as Interface_Scan_Next will report that +association. + +10. Promiscuous mode +==================== + + When running network monitoring tools, e.g., tcpdump, it is +common to enable promiscuous mode on the device, so that all traffic +is seen (instead of seeing only traffic destined for the local host). +The bonding driver handles promiscuous mode changes to the bonding +master device (e.g., bond0), and propagates the setting to the slave +devices. + + For the balance-rr, balance-xor, broadcast, and 802.3ad modes, +the promiscuous mode setting is propagated to all slaves. + + For the active-backup, balance-tlb and balance-alb modes, the +promiscuous mode setting is propagated only to the active slave. + + For balance-tlb mode, the active slave is the slave currently +receiving inbound traffic. + + For balance-alb mode, the active slave is the slave used as a +"primary." This slave is used for mode-specific control traffic, for +sending to peers that are unassigned or if the load is unbalanced. + + For the active-backup, balance-tlb and balance-alb modes, when +the active slave changes (e.g., due to a link failure), the +promiscuous setting will be propagated to the new active slave. + +11. Configuring Bonding for High Availability +============================================= + + High Availability refers to configurations that provide +maximum network availability by having redundant or backup devices, +links or switches between the host and the rest of the world. The +goal is to provide the maximum availability of network connectivity +(i.e., the network always works), even though other configurations +could provide higher throughput. + +11.1 High Availability in a Single Switch Topology +-------------------------------------------------- + + If two hosts (or a host and a single switch) are directly +connected via multiple physical links, then there is no availability +penalty to optimizing for maximum bandwidth. In this case, there is +only one switch (or peer), so if it fails, there is no alternative +access to fail over to. Additionally, the bonding load balance modes +support link monitoring of their members, so if individual links fail, +the load will be rebalanced across the remaining devices. + + See Section 12, "Configuring Bonding for Maximum Throughput" +for information on configuring bonding with one peer device. + +11.2 High Availability in a Multiple Switch Topology +---------------------------------------------------- + + With multiple switches, the configuration of bonding and the +network changes dramatically. In multiple switch topologies, there is +a trade off between network availability and usable bandwidth. + + Below is a sample network, configured to maximize the +availability of the network: + + | | + |port3 port3| + +-----+----+ +-----+----+ + | |port2 ISL port2| | + | switch A +--------------------------+ switch B | + | | | | + +-----+----+ +-----++---+ + |port1 port1| + | +-------+ | + +-------------+ host1 +---------------+ + eth0 +-------+ eth1 + + In this configuration, there is a link between the two +switches (ISL, or inter switch link), and multiple ports connecting to +the outside world ("port3" on each switch). There is no technical +reason that this could not be extended to a third switch. + +11.2.1 HA Bonding Mode Selection for Multiple Switch Topology +------------------------------------------------------------- + + In a topology such as the example above, the active-backup and +broadcast modes are the only useful bonding modes when optimizing for +availability; the other modes require all links to terminate on the +same peer for them to behave rationally. + +active-backup: This is generally the preferred mode, particularly if + the switches have an ISL and play together well. If the + network configuration is such that one switch is specifically + a backup switch (e.g., has lower capacity, higher cost, etc), + then the primary option can be used to insure that the + preferred link is always used when it is available. + +broadcast: This mode is really a special purpose mode, and is suitable + only for very specific needs. For example, if the two + switches are not connected (no ISL), and the networks beyond + them are totally independent. In this case, if it is + necessary for some specific one-way traffic to reach both + independent networks, then the broadcast mode may be suitable. + +11.2.2 HA Link Monitoring Selection for Multiple Switch Topology +---------------------------------------------------------------- + + The choice of link monitoring ultimately depends upon your +switch. If the switch can reliably fail ports in response to other +failures, then either the MII or ARP monitors should work. For +example, in the above example, if the "port3" link fails at the remote +end, the MII monitor has no direct means to detect this. The ARP +monitor could be configured with a target at the remote end of port3, +thus detecting that failure without switch support. + + In general, however, in a multiple switch topology, the ARP +monitor can provide a higher level of reliability in detecting end to +end connectivity failures (which may be caused by the failure of any +individual component to pass traffic for any reason). Additionally, +the ARP monitor should be configured with multiple targets (at least +one for each switch in the network). This will insure that, +regardless of which switch is active, the ARP monitor has a suitable +target to query. + + Note, also, that of late many switches now support a functionality +generally referred to as "trunk failover." This is a feature of the +switch that causes the link state of a particular switch port to be set +down (or up) when the state of another switch port goes down (or up). +Its purpose is to propagate link failures from logically "exterior" ports +to the logically "interior" ports that bonding is able to monitor via +miimon. Availability and configuration for trunk failover varies by +switch, but this can be a viable alternative to the ARP monitor when using +suitable switches. + +12. Configuring Bonding for Maximum Throughput +============================================== + +12.1 Maximizing Throughput in a Single Switch Topology +------------------------------------------------------ + + In a single switch configuration, the best method to maximize +throughput depends upon the application and network environment. The +various load balancing modes each have strengths and weaknesses in +different environments, as detailed below. + + For this discussion, we will break down the topologies into +two categories. Depending upon the destination of most traffic, we +categorize them into either "gatewayed" or "local" configurations. + + In a gatewayed configuration, the "switch" is acting primarily +as a router, and the majority of traffic passes through this router to +other networks. An example would be the following: + + + +----------+ +----------+ + | |eth0 port1| | to other networks + | Host A +---------------------+ router +-------------------> + | +---------------------+ | Hosts B and C are out + | |eth1 port2| | here somewhere + +----------+ +----------+ + + The router may be a dedicated router device, or another host +acting as a gateway. For our discussion, the important point is that +the majority of traffic from Host A will pass through the router to +some other network before reaching its final destination. + + In a gatewayed network configuration, although Host A may +communicate with many other systems, all of its traffic will be sent +and received via one other peer on the local network, the router. + + Note that the case of two systems connected directly via +multiple physical links is, for purposes of configuring bonding, the +same as a gatewayed configuration. In that case, it happens that all +traffic is destined for the "gateway" itself, not some other network +beyond the gateway. + + In a local configuration, the "switch" is acting primarily as +a switch, and the majority of traffic passes through this switch to +reach other stations on the same network. An example would be the +following: + + +----------+ +----------+ +--------+ + | |eth0 port1| +-------+ Host B | + | Host A +------------+ switch |port3 +--------+ + | +------------+ | +--------+ + | |eth1 port2| +------------------+ Host C | + +----------+ +----------+port4 +--------+ + + + Again, the switch may be a dedicated switch device, or another +host acting as a gateway. For our discussion, the important point is +that the majority of traffic from Host A is destined for other hosts +on the same local network (Hosts B and C in the above example). + + In summary, in a gatewayed configuration, traffic to and from +the bonded device will be to the same MAC level peer on the network +(the gateway itself, i.e., the router), regardless of its final +destination. In a local configuration, traffic flows directly to and +from the final destinations, thus, each destination (Host B, Host C) +will be addressed directly by their individual MAC addresses. + + This distinction between a gatewayed and a local network +configuration is important because many of the load balancing modes +available use the MAC addresses of the local network source and +destination to make load balancing decisions. The behavior of each +mode is described below. + + +12.1.1 MT Bonding Mode Selection for Single Switch Topology +----------------------------------------------------------- + + This configuration is the easiest to set up and to understand, +although you will have to decide which bonding mode best suits your +needs. The trade offs for each mode are detailed below: + +balance-rr: This mode is the only mode that will permit a single + TCP/IP connection to stripe traffic across multiple + interfaces. It is therefore the only mode that will allow a + single TCP/IP stream to utilize more than one interface's + worth of throughput. This comes at a cost, however: the + striping generally results in peer systems receiving packets out + of order, causing TCP/IP's congestion control system to kick + in, often by retransmitting segments. + + It is possible to adjust TCP/IP's congestion limits by + altering the net.ipv4.tcp_reordering sysctl parameter. The + usual default value is 3, and the maximum useful value is 127. + For a four interface balance-rr bond, expect that a single + TCP/IP stream will utilize no more than approximately 2.3 + interface's worth of throughput, even after adjusting + tcp_reordering. + + Note that the fraction of packets that will be delivered out of + order is highly variable, and is unlikely to be zero. The level + of reordering depends upon a variety of factors, including the + networking interfaces, the switch, and the topology of the + configuration. Speaking in general terms, higher speed network + cards produce more reordering (due to factors such as packet + coalescing), and a "many to many" topology will reorder at a + higher rate than a "many slow to one fast" configuration. + + Many switches do not support any modes that stripe traffic + (instead choosing a port based upon IP or MAC level addresses); + for those devices, traffic for a particular connection flowing + through the switch to a balance-rr bond will not utilize greater + than one interface's worth of bandwidth. + + If you are utilizing protocols other than TCP/IP, UDP for + example, and your application can tolerate out of order + delivery, then this mode can allow for single stream datagram + performance that scales near linearly as interfaces are added + to the bond. + + This mode requires the switch to have the appropriate ports + configured for "etherchannel" or "trunking." + +active-backup: There is not much advantage in this network topology to + the active-backup mode, as the inactive backup devices are all + connected to the same peer as the primary. In this case, a + load balancing mode (with link monitoring) will provide the + same level of network availability, but with increased + available bandwidth. On the plus side, active-backup mode + does not require any configuration of the switch, so it may + have value if the hardware available does not support any of + the load balance modes. + +balance-xor: This mode will limit traffic such that packets destined + for specific peers will always be sent over the same + interface. Since the destination is determined by the MAC + addresses involved, this mode works best in a "local" network + configuration (as described above), with destinations all on + the same local network. This mode is likely to be suboptimal + if all your traffic is passed through a single router (i.e., a + "gatewayed" network configuration, as described above). + + As with balance-rr, the switch ports need to be configured for + "etherchannel" or "trunking." + +broadcast: Like active-backup, there is not much advantage to this + mode in this type of network topology. + +802.3ad: This mode can be a good choice for this type of network + topology. The 802.3ad mode is an IEEE standard, so all peers + that implement 802.3ad should interoperate well. The 802.3ad + protocol includes automatic configuration of the aggregates, + so minimal manual configuration of the switch is needed + (typically only to designate that some set of devices is + available for 802.3ad). The 802.3ad standard also mandates + that frames be delivered in order (within certain limits), so + in general single connections will not see misordering of + packets. The 802.3ad mode does have some drawbacks: the + standard mandates that all devices in the aggregate operate at + the same speed and duplex. Also, as with all bonding load + balance modes other than balance-rr, no single connection will + be able to utilize more than a single interface's worth of + bandwidth. + + Additionally, the linux bonding 802.3ad implementation + distributes traffic by peer (using an XOR of MAC addresses), + so in a "gatewayed" configuration, all outgoing traffic will + generally use the same device. Incoming traffic may also end + up on a single device, but that is dependent upon the + balancing policy of the peer's 8023.ad implementation. In a + "local" configuration, traffic will be distributed across the + devices in the bond. + + Finally, the 802.3ad mode mandates the use of the MII monitor, + therefore, the ARP monitor is not available in this mode. + +balance-tlb: The balance-tlb mode balances outgoing traffic by peer. + Since the balancing is done according to MAC address, in a + "gatewayed" configuration (as described above), this mode will + send all traffic across a single device. However, in a + "local" network configuration, this mode balances multiple + local network peers across devices in a vaguely intelligent + manner (not a simple XOR as in balance-xor or 802.3ad mode), + so that mathematically unlucky MAC addresses (i.e., ones that + XOR to the same value) will not all "bunch up" on a single + interface. + + Unlike 802.3ad, interfaces may be of differing speeds, and no + special switch configuration is required. On the down side, + in this mode all incoming traffic arrives over a single + interface, this mode requires certain ethtool support in the + network device driver of the slave interfaces, and the ARP + monitor is not available. + +balance-alb: This mode is everything that balance-tlb is, and more. + It has all of the features (and restrictions) of balance-tlb, + and will also balance incoming traffic from local network + peers (as described in the Bonding Module Options section, + above). + + The only additional down side to this mode is that the network + device driver must support changing the hardware address while + the device is open. + +12.1.2 MT Link Monitoring for Single Switch Topology +---------------------------------------------------- + + The choice of link monitoring may largely depend upon which +mode you choose to use. The more advanced load balancing modes do not +support the use of the ARP monitor, and are thus restricted to using +the MII monitor (which does not provide as high a level of end to end +assurance as the ARP monitor). + +12.2 Maximum Throughput in a Multiple Switch Topology +----------------------------------------------------- + + Multiple switches may be utilized to optimize for throughput +when they are configured in parallel as part of an isolated network +between two or more systems, for example: + + +-----------+ + | Host A | + +-+---+---+-+ + | | | + +--------+ | +---------+ + | | | + +------+---+ +-----+----+ +-----+----+ + | Switch A | | Switch B | | Switch C | + +------+---+ +-----+----+ +-----+----+ + | | | + +--------+ | +---------+ + | | | + +-+---+---+-+ + | Host B | + +-----------+ + + In this configuration, the switches are isolated from one +another. One reason to employ a topology such as this is for an +isolated network with many hosts (a cluster configured for high +performance, for example), using multiple smaller switches can be more +cost effective than a single larger switch, e.g., on a network with 24 +hosts, three 24 port switches can be significantly less expensive than +a single 72 port switch. + + If access beyond the network is required, an individual host +can be equipped with an additional network device connected to an +external network; this host then additionally acts as a gateway. + +12.2.1 MT Bonding Mode Selection for Multiple Switch Topology +------------------------------------------------------------- + + In actual practice, the bonding mode typically employed in +configurations of this type is balance-rr. Historically, in this +network configuration, the usual caveats about out of order packet +delivery are mitigated by the use of network adapters that do not do +any kind of packet coalescing (via the use of NAPI, or because the +device itself does not generate interrupts until some number of +packets has arrived). When employed in this fashion, the balance-rr +mode allows individual connections between two hosts to effectively +utilize greater than one interface's bandwidth. + +12.2.2 MT Link Monitoring for Multiple Switch Topology +------------------------------------------------------ + + Again, in actual practice, the MII monitor is most often used +in this configuration, as performance is given preference over +availability. The ARP monitor will function in this topology, but its +advantages over the MII monitor are mitigated by the volume of probes +needed as the number of systems involved grows (remember that each +host in the network is configured with bonding). + +13. Switch Behavior Issues +========================== + +13.1 Link Establishment and Failover Delays +------------------------------------------- + + Some switches exhibit undesirable behavior with regard to the +timing of link up and down reporting by the switch. + + First, when a link comes up, some switches may indicate that +the link is up (carrier available), but not pass traffic over the +interface for some period of time. This delay is typically due to +some type of autonegotiation or routing protocol, but may also occur +during switch initialization (e.g., during recovery after a switch +failure). If you find this to be a problem, specify an appropriate +value to the updelay bonding module option to delay the use of the +relevant interface(s). + + Second, some switches may "bounce" the link state one or more +times while a link is changing state. This occurs most commonly while +the switch is initializing. Again, an appropriate updelay value may +help. + + Note that when a bonding interface has no active links, the +driver will immediately reuse the first link that goes up, even if the +updelay parameter has been specified (the updelay is ignored in this +case). If there are slave interfaces waiting for the updelay timeout +to expire, the interface that first went into that state will be +immediately reused. This reduces down time of the network if the +value of updelay has been overestimated, and since this occurs only in +cases with no connectivity, there is no additional penalty for +ignoring the updelay. + + In addition to the concerns about switch timings, if your +switches take a long time to go into backup mode, it may be desirable +to not activate a backup interface immediately after a link goes down. +Failover may be delayed via the downdelay bonding module option. + +13.2 Duplicated Incoming Packets +-------------------------------- + + NOTE: Starting with version 3.0.2, the bonding driver has logic to +suppress duplicate packets, which should largely eliminate this problem. +The following description is kept for reference. + + It is not uncommon to observe a short burst of duplicated +traffic when the bonding device is first used, or after it has been +idle for some period of time. This is most easily observed by issuing +a "ping" to some other host on the network, and noticing that the +output from ping flags duplicates (typically one per slave). + + For example, on a bond in active-backup mode with five slaves +all connected to one switch, the output may appear as follows: + +# ping -n 10.0.4.2 +PING 10.0.4.2 (10.0.4.2) from 10.0.3.10 : 56(84) bytes of data. +64 bytes from 10.0.4.2: icmp_seq=1 ttl=64 time=13.7 ms +64 bytes from 10.0.4.2: icmp_seq=1 ttl=64 time=13.8 ms (DUP!) +64 bytes from 10.0.4.2: icmp_seq=1 ttl=64 time=13.8 ms (DUP!) +64 bytes from 10.0.4.2: icmp_seq=1 ttl=64 time=13.8 ms (DUP!) +64 bytes from 10.0.4.2: icmp_seq=1 ttl=64 time=13.8 ms (DUP!) +64 bytes from 10.0.4.2: icmp_seq=2 ttl=64 time=0.216 ms +64 bytes from 10.0.4.2: icmp_seq=3 ttl=64 time=0.267 ms +64 bytes from 10.0.4.2: icmp_seq=4 ttl=64 time=0.222 ms + + This is not due to an error in the bonding driver, rather, it +is a side effect of how many switches update their MAC forwarding +tables. Initially, the switch does not associate the MAC address in +the packet with a particular switch port, and so it may send the +traffic to all ports until its MAC forwarding table is updated. Since +the interfaces attached to the bond may occupy multiple ports on a +single switch, when the switch (temporarily) floods the traffic to all +ports, the bond device receives multiple copies of the same packet +(one per slave device). + + The duplicated packet behavior is switch dependent, some +switches exhibit this, and some do not. On switches that display this +behavior, it can be induced by clearing the MAC forwarding table (on +most Cisco switches, the privileged command "clear mac address-table +dynamic" will accomplish this). + +14. Hardware Specific Considerations +==================================== + + This section contains additional information for configuring +bonding on specific hardware platforms, or for interfacing bonding +with particular switches or other devices. + +14.1 IBM BladeCenter +-------------------- + + This applies to the JS20 and similar systems. + + On the JS20 blades, the bonding driver supports only +balance-rr, active-backup, balance-tlb and balance-alb modes. This is +largely due to the network topology inside the BladeCenter, detailed +below. + +JS20 network adapter information +-------------------------------- + + All JS20s come with two Broadcom Gigabit Ethernet ports +integrated on the planar (that's "motherboard" in IBM-speak). In the +BladeCenter chassis, the eth0 port of all JS20 blades is hard wired to +I/O Module #1; similarly, all eth1 ports are wired to I/O Module #2. +An add-on Broadcom daughter card can be installed on a JS20 to provide +two more Gigabit Ethernet ports. These ports, eth2 and eth3, are +wired to I/O Modules 3 and 4, respectively. + + Each I/O Module may contain either a switch or a passthrough +module (which allows ports to be directly connected to an external +switch). Some bonding modes require a specific BladeCenter internal +network topology in order to function; these are detailed below. + + Additional BladeCenter-specific networking information can be +found in two IBM Redbooks (www.ibm.com/redbooks): + +"IBM eServer BladeCenter Networking Options" +"IBM eServer BladeCenter Layer 2-7 Network Switching" + +BladeCenter networking configuration +------------------------------------ + + Because a BladeCenter can be configured in a very large number +of ways, this discussion will be confined to describing basic +configurations. + + Normally, Ethernet Switch Modules (ESMs) are used in I/O +modules 1 and 2. In this configuration, the eth0 and eth1 ports of a +JS20 will be connected to different internal switches (in the +respective I/O modules). + + A passthrough module (OPM or CPM, optical or copper, +passthrough module) connects the I/O module directly to an external +switch. By using PMs in I/O module #1 and #2, the eth0 and eth1 +interfaces of a JS20 can be redirected to the outside world and +connected to a common external switch. + + Depending upon the mix of ESMs and PMs, the network will +appear to bonding as either a single switch topology (all PMs) or as a +multiple switch topology (one or more ESMs, zero or more PMs). It is +also possible to connect ESMs together, resulting in a configuration +much like the example in "High Availability in a Multiple Switch +Topology," above. + +Requirements for specific modes +------------------------------- + + The balance-rr mode requires the use of passthrough modules +for devices in the bond, all connected to an common external switch. +That switch must be configured for "etherchannel" or "trunking" on the +appropriate ports, as is usual for balance-rr. + + The balance-alb and balance-tlb modes will function with +either switch modules or passthrough modules (or a mix). The only +specific requirement for these modes is that all network interfaces +must be able to reach all destinations for traffic sent over the +bonding device (i.e., the network must converge at some point outside +the BladeCenter). + + The active-backup mode has no additional requirements. + +Link monitoring issues +---------------------- + + When an Ethernet Switch Module is in place, only the ARP +monitor will reliably detect link loss to an external switch. This is +nothing unusual, but examination of the BladeCenter cabinet would +suggest that the "external" network ports are the ethernet ports for +the system, when it fact there is a switch between these "external" +ports and the devices on the JS20 system itself. The MII monitor is +only able to detect link failures between the ESM and the JS20 system. + + When a passthrough module is in place, the MII monitor does +detect failures to the "external" port, which is then directly +connected to the JS20 system. + +Other concerns +-------------- + + The Serial Over LAN (SoL) link is established over the primary +ethernet (eth0) only, therefore, any loss of link to eth0 will result +in losing your SoL connection. It will not fail over with other +network traffic, as the SoL system is beyond the control of the +bonding driver. + + It may be desirable to disable spanning tree on the switch +(either the internal Ethernet Switch Module, or an external switch) to +avoid fail-over delay issues when using bonding. + + +15. Frequently Asked Questions +============================== + +1. Is it SMP safe? + + Yes. The old 2.0.xx channel bonding patch was not SMP safe. +The new driver was designed to be SMP safe from the start. + +2. What type of cards will work with it? + + Any Ethernet type cards (you can even mix cards - a Intel +EtherExpress PRO/100 and a 3com 3c905b, for example). For most modes, +devices need not be of the same speed. + + Starting with version 3.2.1, bonding also supports Infiniband +slaves in active-backup mode. + +3. How many bonding devices can I have? + + There is no limit. + +4. How many slaves can a bonding device have? + + This is limited only by the number of network interfaces Linux +supports and/or the number of network cards you can place in your +system. + +5. What happens when a slave link dies? + + If link monitoring is enabled, then the failing device will be +disabled. The active-backup mode will fail over to a backup link, and +other modes will ignore the failed link. The link will continue to be +monitored, and should it recover, it will rejoin the bond (in whatever +manner is appropriate for the mode). See the sections on High +Availability and the documentation for each mode for additional +information. + + Link monitoring can be enabled via either the miimon or +arp_interval parameters (described in the module parameters section, +above). In general, miimon monitors the carrier state as sensed by +the underlying network device, and the arp monitor (arp_interval) +monitors connectivity to another host on the local network. + + If no link monitoring is configured, the bonding driver will +be unable to detect link failures, and will assume that all links are +always available. This will likely result in lost packets, and a +resulting degradation of performance. The precise performance loss +depends upon the bonding mode and network configuration. + +6. Can bonding be used for High Availability? + + Yes. See the section on High Availability for details. + +7. Which switches/systems does it work with? + + The full answer to this depends upon the desired mode. + + In the basic balance modes (balance-rr and balance-xor), it +works with any system that supports etherchannel (also called +trunking). Most managed switches currently available have such +support, and many unmanaged switches as well. + + The advanced balance modes (balance-tlb and balance-alb) do +not have special switch requirements, but do need device drivers that +support specific features (described in the appropriate section under +module parameters, above). + + In 802.3ad mode, it works with systems that support IEEE +802.3ad Dynamic Link Aggregation. Most managed and many unmanaged +switches currently available support 802.3ad. + + The active-backup mode should work with any Layer-II switch. + +8. Where does a bonding device get its MAC address from? + + When using slave devices that have fixed MAC addresses, or when +the fail_over_mac option is enabled, the bonding device's MAC address is +the MAC address of the active slave. + + For other configurations, if not explicitly configured (with +ifconfig or ip link), the MAC address of the bonding device is taken from +its first slave device. This MAC address is then passed to all following +slaves and remains persistent (even if the first slave is removed) until +the bonding device is brought down or reconfigured. + + If you wish to change the MAC address, you can set it with +ifconfig or ip link: + +# ifconfig bond0 hw ether 00:11:22:33:44:55 + +# ip link set bond0 address 66:77:88:99:aa:bb + + The MAC address can be also changed by bringing down/up the +device and then changing its slaves (or their order): + +# ifconfig bond0 down ; modprobe -r bonding +# ifconfig bond0 .... up +# ifenslave bond0 eth... + + This method will automatically take the address from the next +slave that is added. + + To restore your slaves' MAC addresses, you need to detach them +from the bond (`ifenslave -d bond0 eth0'). The bonding driver will +then restore the MAC addresses that the slaves had before they were +enslaved. + +16. Resources and Links +======================= + + The latest version of the bonding driver can be found in the latest +version of the linux kernel, found on http://kernel.org + + The latest version of this document can be found in the latest kernel +source (named Documentation/networking/bonding.txt). + + Discussions regarding the usage of the bonding driver take place on the +bonding-devel mailing list, hosted at sourceforge.net. If you have questions or +problems, post them to the list. The list address is: + +bonding-devel@lists.sourceforge.net + + The administrative interface (to subscribe or unsubscribe) can +be found at: + +https://lists.sourceforge.net/lists/listinfo/bonding-devel + + Discussions regarding the development of the bonding driver take place +on the main Linux network mailing list, hosted at vger.kernel.org. The list +address is: + +netdev@vger.kernel.org + + The administrative interface (to subscribe or unsubscribe) can +be found at: + +http://vger.kernel.org/vger-lists.html#netdev + +Donald Becker's Ethernet Drivers and diag programs may be found at : + - http://web.archive.org/web/*/http://www.scyld.com/network/ + +You will also find a lot of information regarding Ethernet, NWay, MII, +etc. at www.scyld.com. + +-- END -- diff --git a/README.md b/README.md new file mode 100644 index 0000000..5e90045 --- /dev/null +++ b/README.md @@ -0,0 +1,22 @@ +[![Build Status](https://travis-ci.org/iputils/iputils.svg?branch=master)](https://travis-ci.org/iputils/iputils) +[![Coverity Status](https://scan.coverity.com/projects/1944/badge.svg?flat=1)](https://scan.coverity.com/projects/1944) + +The iputils package is set of small useful utilities for Linux networking. + +These tools are included in iputils +- arping +- clockdiff +- ipg +- ping +- rarpd +- rdisc +- tftpd +- tracepath +- traceroute6 + +If you still use [old version](http://www.skbuff.net/iputils/), please consider moving forward to new releases placed here. + +This version also fully supports glibc, uClibc and musl-libc. + + + diff --git a/RELNOTES.old b/RELNOTES.old new file mode 100644 index 0000000..b2dabda --- /dev/null +++ b/RELNOTES.old @@ -0,0 +1,945 @@ +NO LONGER USED! + +Please consult with https://github.com/iputils/iputils or git history. + +Kept for historical purposes. + +[s20161105] + +David Heidelberg (1): + ping: eliminate deadcode & simplify + +Jan Synacek (5): + ping: do not allow oversized packets to root + correctly initialize first hop + ping: fix ping -6 -I + arping,doc: fix documentation of -I + ping: fix error message when getting EACCES from connect() + +Karl-Philipp Richter (2): + renamed INSTALL to INSTALL.md + (re)structured INSTALL.md and transformed into markdown; added hint that installation into prefix has to be done with DESTDIR make variable and that there's no prefix support in configure, close #21 + +Markos Chandras (2): + ping: Silence GCC warnings when building with -fstrict-aliasing + tftpd: Drop supplementary groups for root + +Martin Bark (1): + libgcrypt: fix static linking + +Olof Sjödin (1): + doc: Inserted a missing word + +Pavel Šimerda (8): + tracepath6: avoid redundant family variable + tracepath: borrow everything good from tracepath6 + tracepath: switch to dual-stack operation + tracepath: remove now redundant tracepath6 + docs: fix parallel build of manpages + ping: remove assignments of values that are never read + docs: remove references to ping6 and traceroute6 + ping: work with older kernels that don't support ping sockets + +Robert Schiele (2): + Revert "ping_common.c: fix message flood when EPERM is encountered in ping" + reorder -I option parsing + +依云 (1): + ping: also bind the ICMP socket to the specific device + + +[s20160308] + +Aaro Koskinen (1): + use syntax compatible with busybox date in Makefile + +Chris Morrow (1): + 'admin prohibited' should print !X not !S. + +David Heidelberg (3): + Makefile: use #define as in previous code changes + iputils-s20150815 + doc/Makefile: require bash, because we use pushd and popd + +David McMackins II (1): + ping: status() now returns received/transmitted instead of trans/recv + +Felix Janda (1): + ping: don't mess with internals of struct msghdr + +Jan Synacek (5): + tracepath,doc: fix corrupted tag + doc: ping: add missing options and remove ping6 + ping: always use POSIX locale when parsing -i + doc: don't timestamp manpages by default + ninfod: remove unused variables + +Jason A. Donenfeld (1): + ping: ICMP error replies while errno < 0 is a hard error + +Kylie McClain (1): + Fix building with musl + +Martin Bark (1): + ping: link against libm + +Nikos Mavrogiannopoulos (10): + made ping functions protocol independent + Allow ping to use IPv6 addresses + if IPv4 resolving fails fallback to ping6 + ping: in usage print the 'ping -6' options as well + ping: allow option -4 which forces IPv4 + combine sock and errno into a single structure + This patch allows running ping and ping6 without root privileges on + use better names for socket variables + travis.yml: install nettle-dev + Allow using nettle instead of libgcrypt for MD5 + +Pavel Šimerda (14): + avoid compiler warning caused by snapshot.h + make `getaddrinfo()` and `getnameinfo()` usage consistent + enable IDN by default + ping: perform dual-stack ping by default + remove IPV4_TARGETS and IPV6_TARGETS + ping: remove obsolete preprocessor directives + ping: avoid name clashes between IPv4 and IPv6 code + ping: merge all ping header files into a single one + ping: merge `ping6` command into `ping` + ping: refactor ping options + ping: refactor ping socket code + ping: merge IPv4 and IPv6 `pr_addr()` + ping: fix defines and libs in Makefile + ping: handle single protocol systems + +Peter Dave Hello (1): + Use svg instead of png to get better image quality + +Salvatore Mesoraca (1): + iputils ping/ping6: Add a function to check if a packet is ours + +YOSHIFUJI Hideaki (9): + ping: Add to fix compilation error. + ping6: Use GNUTLS API directly for MD5. (v2) + ping6: Use libgcrypt instead of gnutls for MD5. + ninfod: Regenerate configure by autoconf-2.69. + ninfod: libgcrypt support. + spec: Configure before building ninfod. + spec: Fix date in %changelog. + make,spec: Add rpm target. + ping,ping6 doc: More description on CAP_NET_RAW usage. + + +[s20150815] + +Aaro Koskinen (1): + use syntax compatible with busybox date in Makefile + +David Heidelberg (1): + Makefile: use #define as in previous code changes + +David McMackins II (1): + ping: status() now returns received/transmitted instead of trans/recv + +Felix Janda (1): + ping: don't mess with internals of struct msghdr + +Jan Synacek (1): + tracepath,doc: fix corrupted tag + +Nikos Mavrogiannopoulos (10): + made ping functions protocol independent + Allow ping to use IPv6 addresses + if IPv4 resolving fails fallback to ping6 + ping: in usage print the 'ping -6' options as well + ping: allow option -4 which forces IPv4 + combine sock and errno into a single structure + This patch allows running ping and ping6 without root privileges on + use better names for socket variables + travis.yml: install nettle-dev + Allow using nettle instead of libgcrypt for MD5 + +Pavel Šimerda (12): + avoid compiler warning caused by snapshot.h + make `getaddrinfo()` and `getnameinfo()` usage consistent + enable IDN by default + ping: perform dual-stack ping by default + remove IPV4_TARGETS and IPV6_TARGETS + ping: remove obsolete preprocessor directives + ping: avoid name clashes between IPv4 and IPv6 code + ping: merge all ping header files into a single one + ping: merge `ping6` command into `ping` + ping: refactor ping options + ping: refactor ping socket code + ping: merge IPv4 and IPv6 `pr_addr()` + +Peter Dave Hello (1): + Use svg instead of png to get better image quality + +Salvatore Mesoraca (1): + iputils ping/ping6: Add a function to check if a packet is ours + +YOSHIFUJI Hideaki (9): + ping: Add to fix compilation error. + ping6: Use GNUTLS API directly for MD5. (v2) + ping6: Use libgcrypt instead of gnutls for MD5. + ninfod: Regenerate configure by autoconf-2.69. + ninfod: libgcrypt support. + spec: Configure before building ninfod. + spec: Fix date in %changelog. + make,spec: Add rpm target. + ping,ping6 doc: More description on CAP_NET_RAW usage. + + +[s20140519] + +David Gibson (3): + arping: Avoid confusing local names + arping: Use monotonic clock for timeouts + arping: Clarify and correct interaction of -c and -w options + +David Heidelberger (3): + rdisc: ifdef only apply on historic glibc < 2 + get rid of unused lint and copyright array + cleanup, get rid of if(1), #if 0 and constify on + +Jan Synacek (3): + ninfod: Fix more unused variables. + arping: fix arping hang if SIGALRM is blocked + ping_common.c: fix message flood when EPERM is encountered in ping + + +[s20140420] + +David Heidelberger (1): + improve autobuild bot configuration + +Mike Frysinger (5): + improve gitignore + doc: fix parallel build of html/man pages + ping6: allow disabling of openssl support + fix handling of CFLAGS + tftpd: check return value of set*id calls + + +[s20140419] + +Arjan van de Ven (1): + ping6: Fix build command line argument with gnutls. + +Bjørn Mork (1): + tracepath: return correct number of hops + +Christophe Le Roy (1): + ping: flush stdout when a truncated response is received + +David Fries (1): + Only emit an audible ping when requested (including flooding). + +David Heidelberger (13): + add missing limits.h + in case that HZ is undefined, define it + fix include paths, now compile with both glibc and musl + protocol/timed.h is no longer required + fix ifdef to apply only on uclibc and old glibc + replace non-POSIX compilant caddr_t with char * + replace u_TYPE with compilant unsigned TYPE + add .gitignore and .travis.yml + ping_common.c: fix typo + fix tracepath docs + Makefile: set CC only if it's not defined before + add README.md + .travis.yml, improve build testing + +Hendrik Lönngren (1): + arping: return immediately + +Jan Synacek (1): + ping doc: Fix typo. + +Jeremie Koenig (1): + replace gethostbyname with gethostbyname2 + +Matija Nalis (1): + Disable DNS-lookup on every ping + +Mike Frysinger (1): + tracepath, tracepath6: re-use printf return in print_host + +Noah Meyerhans (1): + remove bogus check required for < 2.4.9 kernels + +YOSHIFUJI Hideaki (5): + rdisc: Fix memory leakage in initifs() in error path. + ninfod: Clean up signal/logfile handling + ninfod: Open pidfile exclusively for write. + ping, ping6: fix building with older linux headers that don't define SO_MARK + tracepath, tracepath6: Support -m (maximum hops) option. + + +[s20121221] + +YOSHIFUJI Hideaki (14): + ninfod: Use unsigned int for digest. + ninfod: nanosleep(3) needs . + ninfod: Too many arguments for syslog(3)/fprintf(3) via DEBUG(). + ninfod: Fix several warnings on ununsed variables. + ping6: Print unknown ICMP type. + ping6: Fix flowlabel switch (-F option). + arping: Fix sysfs decimal/hexadecimal parser for libsysfs support. + ping6: Use GNU TLS by default. + ninfod: Fix memory leakage in error path. + ninfod: Fix off-by-one error to check possible programming error (again). + ninfod: Do not expose freed buffer to caller. + ping6: Ensure to initialize msghdr. + ninfod: Support GNU TLS. + ninfod: Allow printing usage without permission errors. + + +[s20121207] + +YOSHIFUJI Hideaki (2): + RELNOTES: Typos. + ping,ping6: Check outgoing device only if specified. + + +[s20121205] + +Jan Synacek (1): + ping,tracepath doc: Fix missing end tags. + +YOSHIFUJI Hideaki (35): + tracepath6: packet length option (-l) did not have any effect. + tracepath,tracepath6: Fix pktlen message. + tracepath,tracepath6: Use calloc(3) instead of using stack. + tracepath6: Ignore families other than IPv4 and IPv6. + ping6: Improve randomness of NI Nonce. + tracepath,tracepath6 doc: Fix default pktlen. + ping,rdisc: Optimize checksumming. + makefile: Static link support for crypto, resolv, cap and sysfs. + doc: Ajdust spaces around sqare brackets. + ping,rdisc: Use macro to get odd byte when checksumming. + ping6: Do not try to free memory pointed by uninitialized variable on error path. + arping: Allow building without default interface. + arping: No default interface by default. + arping: Allow printing usage without permission errors. + ping,ping6: Allow printing usage without permission errors. + ping,ping6: Fix cap_t leakage. + arping,ping,ping6: Do not ideologically check return value from cap_free,cap_{set,get}_flag(). + arping: Fix sysfs_class leakage on error path. + arping: Some comments for new functions for finding devices support. + arping: Typo in type declaration. + makefile: Use call function for external libraries. + makefile: Add more comments. + arping: Ensure to fail if no appropriate device found with sysfs. + arping: Enforce user to specify device (-I) if multiple devices found. + Makefile: parameterize options for linking libraries. + Makefile: Use shell function instead if backquotes. + Makefile: Ensure to have same date when making snapshot. + spec: Maintainer does not use ipsec.spec. + spec: partially sync with fedora. + Makefile: Bump date in iputils.spec as well. + spec: Add exmple lines for suid-root installation + spec: Sort changelog. + ping: Exit on SO_BINDTODEVICE failure. + ping: Warn if kernel has selected source address from other interface. + ping: Clarify difference between -I device and -I addr. + + +[s20121126] + +YOSHIFUJI Hideaki (5): + tracepath: Repair tracepath without -p option. + tracepath,tracepath6: -p option in usage. + ping,ping6: Use MAX_DUP_CHK directly, not using mx_dup_chk variable. + ping,ping6: Abstract received bitmap macros/definitions. + ping,ping6: Use __u64 or __u32 for bitmap. + + +[s20121125] + +YOSHIFUJI Hideaki (30): + ping6: Use IN6_IS_ADDR_UNSPECIFIED() instead of our own helper function. + ping6 doc: Explicitly describe ping6 is IPv6 version if ping. + ping6: Deprecate source routing by default (RFC5095). + ping6: Use RFC3542 functions and definition for source routing. + ping6: Introduce niquery_is_enabled() for readability. + arping doc: interface is optional (-I option). + ping: Eliminate dirty hack to cope with ancient egcs bug. + Makefile: Fix missing right parenthes in comment. + arping: Fix build failure with USE_SYSFS=yes and/or WITHOUT_IFADDRS=yes + arping: Unify source files. + arping: Reorder functions and comment out unsued code. + arping,ping,ping6,tracepath,traceroute6 Makefile: Support static link of libidn by USE_IDN=static. + Makefile: Minimize statically linked libraries. + ping6: Do not clear seq check array twice for NI. + ping6: Use MD5_DIGEST_LENGTH instead of magic value 16. + ping6: Introduce helper functions for nonce in NI. + ping6: Introduce NI_NONCE_SIZE macro instead of magic value 8. + ping6: Ensure to call srand() to get some randomness in NI Nonce. + ping6: Generate different NI Nonce in each NI Query (Memory version). + ping6: Generate different NI Nonce in each NI Query (MD5 version). + ping6: Cache NI Nonce. + ping6: Print 'sequence number' embedded in NI Nonce. + ninfod: Do noy try to memcpy to self. + ninfod Makefile: More precise dependencies. + ninfod: Discard multicat packet outside linklocal scope. + ninfod: Apply default policy to refuse queries from global addresses. + ninfod: Normalize timespec for delay. + ninfod: Fix double-free without pthreads. + ninfod: Do not mix output from multiple threads. + ninfod: Employ internal buffer in stderrlog() for common case. + + +[s20121121] + +Jan Synacek (2): + ping,ping6: Add newline to error message. + ping: Don't free an unintialized value. + +YOSHIFUJI Hideaki (31): + arping,clockdiff,ping,rarpd,rdisc,traceroute6 doc: s/CAP_NET_RAWIO/CAP_NET_RAW/. + ping,ping6: Do not assume radix point is denoted by '.' (-i option). + arping,ping,ping6,rdisc,traceroute6: Fix version string. + makefile: Give -fno-strict-aliasing to compiler by default. + ping6: Use SCOPE_DELIMITER. + Makefile: Remove -lm from ADDLIB. + rdisc_srv,Makefile: Fix build. + rdisc_srv,Makefile: Build rdisc_srv with make all. + arping: set_device_broadcast() does not need to store return value of sub-functions. + arping,Makefile: Make default interface configurable. + arping: Do not allow empty device name (-I option). + arping: Introduce check_ifflags() helper function. + arping: Introduce device structure to hold output device information. + arping: ALlow no default interface and select one by getifaddrs(). + arping: Introduce 2nd (legacy) method to select interface by ioctls. + arping,Makefile: Allow build without getifaddrs() with WITHOUT_IFADDRS=yes. + Makefile: Use $< instead of $^ to complile C source code. + ping,ping6: Reorder command-line options in alphabetical order. + ping6: Show suboptions for Node Information Queries if -N suboption is invalid. + ping,ping6 doc: Readability for TOS (-Q) option. + rdisc: Missing new line after usage. + rdisc: Make rdisc with responder support if configured. + Makefile: distclean depends on clean. + Makefile: Default to -O3. + Makefile: Minimize options to gcc. + Makefile: Add rule to build assembly files. + arping,Makefile: 3rd legacy implementation to check network devices. + arping: Less ifdefs. + rdisc doc: Document -r, -p and -T options. + ping6: NI Subjecet address did not work (-N subject-{ipv6,ipv4] suboptions). + ping6: Ensure to detect subject type conflicts. + + +[s20121114] + +Jan Synacek (2): + clockdiff: remove unused variable + ping: Wrap SO_BINDTODEVICE with the correct capability. + +YOSHIFUJI Hideaki (13): + ping: IP_MULTICAST_IF does not need CAP_NET_RAW. + ping6: Check ranges of flowlabel (-F option) and tclass (-Q option) arguments. + ping6: Accept 0x-notation for flowlabel (-F option) and tclass (-Q option) arguments. + ping,ping6: Manual update regarding -F, -Q and -N option. + arping,ping,ping6: Defer exitting to allow users to see usage. + arping,ping,ping6,ninfod: Change euid to uid (non-root) even if capabiliy is enabled. + ninfod: Add configure. + ninfod: libcap support to drop capabilities. + ninfod: Add run as user (-u user) option. + ninfod: Fix usage message. + arping,clockdiff,rarpd,rdisc,tftpd: Change RFC source to tools.ietf.org. + ninfod: Add ninfod(8) manpage. + makefile: Add ninfod, distclean targets. + + +[s20121112] + +Sergey Fionov (1): + ping,ping6: Fallback to numeric addresses while exiting + +YOSHIFUJI Hideaki (18): + ping,ping6: Rework capability support and Make sure -m and -I options work. + ping,tracepath: Spelling fixes in manpages. + ping,ping6: Fix integer overflow with large interval value (-i option). + clockdiff: Make it work with large pid. + ping,ping6: Make in_pr_addr volatile. + arping: Do not quit too early with large deadline value (-w option). + arping: Maintain minimum capabilities for SO_BINDTODEVICE(-I option). + ping: Fix recorded route comparison. + arping: Use getifaddrs() to get broadcast address. + ping6: Fix typo in error message. + ping6: Generate NI Group Address and Subject Name at once. + ping,ping6: Unmask signals on start-up. + arping: Build with USE_CAP=no. + arping,ping,ping6,tracepath,tracepath6,traceroute6: Experimental IDN support. + ping6: IDN support for the Subject Name in NI Query. + tracepath,tracepath6: Introduce -p option for port. + ping6: Add missing definitions/declarations for flowlabel management (-F option). + makefile: Do not include merge commits in RELNOTES. + + +[s20121106] + +YOSHIFUJI Hideaki (5): + ninfod: Attatch configure and renew config.h.in. + makefile: clean-up + tracepath6: Print reason on getadrinfo() failure. + ping,ping6: Fix hang with -f option. + ping: Make sure to print C if checksum failed with -f option. + + +[s20121011] + +Jan Synacek (2): + ping,ping6: Defer the dropping if the "-m" is specified and correct capability is set. + ping: Fix typo in echo reply + +Ole Bjorn Hessen (1): + ping: report outstanding packets before sending next packet + +YOSHIFUJI Hideaki (32): + ping,ping6: Add -D to synopsis. + ping: More icmp code descriptions. + ping,ping6: Hide ipg/ewma info without packets received. + ping6: Remove unused variable. + ping6: Help for -N suboptions. + tracepath,tracepath6: Use argument type of int for field width specifier. + clockdiff: Call nice() before changing effective uid. + rdisc: Use fputs() instead of fprintf() to shut up gcc warning. + rarpd: Check return value of chdir(). + makefile: Introduce new variable for capability support. + ping,ping6: Check return value of write(2) for stdout. + ping6,tracepath,tracepath6: Do not dereference type-punned pointer directly. + Makefile: host changed from takos to pleiades. + ping6: Provide enough buffer for dn_comp() and make NI Query with Name subject work. + ping6: Consolidate error path of niquery_option_subject_name_handler(). + ninfod: Node Information Query (RFC4620) daemon from USAGI Project. + ninfod: struct in6_pktinfo requires -D_GNU_SOURCE. + ninfod: Use %zu format string for size_t variable. + ninfod: Add missing entry for ENABLE_SUPTYPES in config.h.in. + ninfod: Support newer environment supporting RFC3542. + ninfod: Fix format string for string returned from strerror(3). + ninfod: Check return value of fscanf(3). + ninfod: Fix off-by-one error to check possible programming error. + ninfod: Add datarootdir. + ninfod: Use __func__ instead of __FUNCTION__. + ninfod: Introduce ARRAY_SIZE macro for counting number of elements in an array. + ninfod: Delete ninfod.sh by make distclean, not by make clean. + ping6: Do not try to use result buffer when dn_comp(3) failed. + ping,ping6: ifdef guard for inline function for capability support and fix build with USE_CAP=no. + makefile: Do not use "-llib" dependency. + arping: build without sysfs support (USE_SYSFS=no). + +Ángel González (1): + iputils: Add capability dropping + + +[s20101006] + +Chris Caputo (1): + ping,ping6: avoid gethostbyaddr during ping flood. + +Paul Martin (1): + arping: Set correct broadcast address. + +YOSHIFUJI Hideaki (4): + tracepath: Fix some small typos in tracepath.sgml. + ping: Fix resource consumption triggered by specially crafted ICMP Echo Reply (CVE-2010-2529) + Makefile: migrate main machine from beatrice to takos. + Makefile: Use newer git subcommand style instead of git-subcommand. + + +[s20100418] + +YOSHIFUJI Hideaki (28): + ping6: Use IPV6_TCLASS to set outgoing traffic class if available. + ping: Make build_echo(), gather_statistics() more generic. + ping6: Experimental support for Node Information Queries (RFC4620). + ping: simplify usage hint. + ping: Rename constant names + Extend -N option for NI Query options. + ping6: Make length-check qtype-specific. + ping6: Remove too many spaces between names. + ping6: ping6_niquery.h needs asm/byteorder.h. + ping6: Support Qtypes for IPv6/IPv4 Addresses. + ping6: Split pr_niquery_reply(). + ping6: Handle ICMPv6 code in NI Reply. + ping6: Add subject-ipv6 and subject-ipv4 NI sub-option for subject address. + ping6: Support subject name. + ping6: Free old memory when reassign pointers. + ping6: Always enable IPv6 Node Information Queries. + makefile: Do not always link libresolv and libcrypto. + ping,traceroute6,clockdiff: Enlarge hostname buffer. + ping6: do not allow too large packet size by -s option. + ping: needless space when printing usage. + rdisc: Fix typo in error message. + rdisc: Allow multiple addresses on one interface. + arping: Support link-layer type with larger link-layer address. + tracepath6: resolve target even if -n option is supplied. + tracepath,tracepath6: sync tracepath and tracepath6. + tracepath6: Make it more protocol independent. + + +[s20100214] + +Jamal Hadi Salim (2): + ping: ping by mark + ping: ping by mark doc update + +Jamie Le Tual (1): + ping: set un.echo.id to network byte order + +YOSHIFUJI Hideaki (11): + [PING6,TRACEROUTE6]: Ignore error in setting IPV6_CHECKSUM socket option for ICMPv6 socket. + [PING6]: Use if_nametoindex() to convert ifname to ifindex. + [PING6]: Allow to specify source address with interface in a single -I option. + ping6: Try using IPV6_PKTINFO sticky option to specify outgoing interface. + rdisc: Use FOPEN_MAX if OPEN_MAX is undefined. + ping6: Fix source routing with source interface set. + ping,ping6: Don't print extra ', ' in finish(). + tracepath: Fix documentation typo. + Use sysconf(_SC_OPEN_MAX) instead of OPEN_MAX. + ping,ping6: Add -D option to print timestamp. + + +[s20071127] + +John Heffner (6): + [iputils] tracepath: Add length flag to set initial MTU. + [iputils] tracepath: Add documentation for the -l flag. + [iputils] tracepath: Use PMTUDISC_PROBE mode if it exists. + [iputils] tracepath: Document -n flag. + [iputils] tracepath: Fix asymm messages. + [iputils] tracepath: Re-probe at same TTL after MTU reduction. + +YOSHIFUJI Hideaki (8): + [DOC]: Delete duplicated lines in RELNOTES. + Fix white space errors. + [CLOCKDIFF,PING,RDISC,TRACEROUTE6]: Support uClibc. + [RARPD]: Fixed several signedness issues for char strings. + [PING]: Use inet_pton() instead of sscan(). + [PING6]: Use IN6_IS_ADDR_xxx() macro. + [MAKEFILE]: Change authorized host to push snapshots. + [MAKEFILE]: Use git-archive instead of git-tar-tree. + + +[s20070202] + +Mike Frysinger (2): + Use socklen_t in all the right places. + [IPG]: handle pktgen setup in newer kernels. + +Mitsuru Chinen (2): + [CLOCKDIFF]: Fix compilation errors about labels at end of compound statements. + [PING6]: Use getaddrinfo() for the name resolution of intermediate nodes. + +YOSHIFUJI Hideaki (9): + [MAKEFILE] Remove unused -I../include + [TRACEPATH] Print usage if we met incorrect option. + [PING6]: Fix compilation error with glibc-2.4 and later. + [PING6]: Use getaddrinfo() to allow scoped addresses + [PING6]: Ensure not to reverse-lookup if target is numeric address. + + +[s20060512] + +YOSHIFUJI Hideaki: + [BUILD] Build with standard headers. + [ARPING,PING6] Build fix for some old systems. + + +[s20060425] + +YOSHIFUJI Hideaki: + [TRACEROUTE6] Fix ICMPv6 type printing with -v option + [TRACEROUTE6] Mark ICMPv6 messages as known + [DOC] Maintainer / Contact change + [PING6,TRACEPATH6,TRACEROUTE6] Define SOL_IPV6,SOL_ICMPV6 where needed + [TRACEROUTE6] Fix source/destination address with -v option + [PING6,TRACEPATH6,TRACEROUTE6] Use new RFC3542 advanced API if available + [RDISC] Use proper type for is_directly_connected() + [PING,PING6] Use proper type for printf() + [TRACEROUTE6] Fix inet_pton() error handling + [TRACEROUTE6] Use minimum format if 0 is specified for datalen + [TRACEROUTE6] Optimize datalen sanity checking code + [TRACEPATH6] Use getaddrinfo() to allow scoped addresses + [RDISC] Use strerror(errno) instead of sys_errlist[errno] + [PING,PING6] Avoid using __constant_htons() if it is really needed + [TRACEPATH6] Fix format for subseconds + [ARPING,CLOCKDIFF,PING,PING6,TRACEROUTE6] Check return value from setuid(). + [PING,PING6] ensure to initialize msg. + [MAKEFILE] Make snapshot using git + + +[020927] +* arping.sgml, some options were forgotten. +* send seqno in network byte order. Me. +* Mads Martin J�rgensen Recursive citation: +"On request of Mads Martin J�rgensen I've added manpages +pregenerated from the Docbook sources. One could argue it is redundant +when the Docbook sources are also there, but the argument of not having +to install Docbook on a very small system to get the man pages was +convinving enough to me. To quote Mads Martin: "How would a system +be without a man page for ping?" + As a chilidish revenge from my side enjoy with cyrillic date in these + man pages. :-) +* Ken Cox . Bogus definition of SOCK_DRGAM&SOCK_STREAM on mips. +* Error returned from recvmsg() resulted in a bogus printout in traceroute6. Me. +* Use IPV6_CHECKSUM on icmp socket in traceroute6. Me. +* Noah L. Meyerhans Fix to doc. +!* Noah L. Meyerhans What is the problem with "long" triptime? +! Reporter does not respond. _Malignantly_. +* Thomas 'Dent' Mirlacher Ping did not exit sometimes! +* Add option -W to override default 10 second linger timeout. Me. +* Mads Martin J�rgensen : ping should not bind to autoselected + source address, it used to work when routing changes. Return classic + behaviour, option -B is added to enforce binding. +* Pekka Savola Forgotten \n messing output of ping6. +* Noah L. Meyerhans traceroute6 -q 1 did not work. +* Pekka Savola various sizeof() cleanups in traceroute6.c +* "Dmitry V. Levin" wrote: + > ping (as well as other utilities) may open raw socket with descriptor <=2; + > In case of suid-root, it can be used by malicious user to send data to + > this raw socket. + > + > Yes, modern glibc and some kernels have workaround for it, but + > IMHO iputils shouldn't rely on this feature. + Taken into account, but no changes made. +* "Tilman Heinrich" said some scripts are broken + when word "packet" disappeared from "100% packet loss". Despite of + the inarguable fact that such scripts are truly mad and deserve breaking + (sigh... exit codes are too smart concept for script writers, I guess), + I have to recognize removing this word carrying zero information + was not enough motivated. Returned. +* ping used to retry forever when seeing ENOBUFS/ENOMEM without explicitly + given deadline. Being logically correct it is bad in practice f.e. when + pinging buggy device which locked up with some packets in queue. + So, retry for a finite time... let is be lingertime. Fair? Me. +* Two "messages" are sent to rpm maintainers to make their wrong patches + failed. +* Fix from RH iputils-20001007-deadline.patch. It was lost in the latest + rpms btw. +* Dax Kelson : added _unsupported_ option to comppile + rdisc_srv. + +[020124] +* Michal Kochanowicz typos in tracepath.8 +* Michael Wardle : undo silly change of ss000305 + (printing rtt in some funny units). Michael noticed that "sec" is not + standard abbreviation for time units (bullshit, of course), but real concern + is that it is more difficult to interpret with a neglibible improvement + to appearance. So, do this as expected: in "ms". +* Documentation. Wow! I did it. man pages are disassembled to docbook, + audited wrt real state, edited... and promised to be maintained + in sync with the state of utilities. + +[011202] +* Utz Bacher Bitops in ping6 were wrong + on bigendian machines. Wow, luckily I forgot to acknowledge that patch + of 010805 which has gotten rid of kernel bitops and did this so wrongly. +* Michael Bakunin (:-)) + found mud in tftpd.c, it will crash when directory supplied in argument + is longer ~512 symbols. +* Alexandr D. Kanevskiy : buffer overflow + in clockdiff. Very stupid one, the overflowed buffer even was not used. :-) +* Alexandr D. Kanevskiy : shit! Code recognizing + kernels with broken IP_RECVERR for raw sockets depended on race + and accused even good kernel of being buggy. :-) + +[011002] +* Stepan Koltsov , tracepath/tracepth6 segfaulted when + used without address. +* Alexandr D. Kanevskiy : arping printed + "permission denied" instead of showing help page to non-superuser. + +[010824] +* Alexandr D. Kanevskiy : ping compiled + for linux-2.4 forgot to send the second packet, when used with linux-2.2 +* Chris Evans : buffer overflow in traceroute6. + datalen was messed: counting header in half of places. + Funny, looking into LBL traceroute, it is even worse :-) +* Alexandr D. Kanevskiy : relayed patches + by Solar_Diz. Only missing description of option -q is accepted. +* ping6 printed wrong mtu. +* Alexandr D. Kanevskiy : -Werror is removed. + Newer gcc are buggy and generates some wrong warnings about + uninitalized variables, which are evidently initialized. + +[010805] +* Some news from Pekka Savola around setting tos bits. +* arping: broadcast-only mode by Ard van Breemen +* ping6/traceroute6: parse ICMP errors with extension headers (me) + traceroute6 works with size > mtu now. Nice. +* ping: Erik Quanstrom . Serious patch. + ping interval timer was not very broken, but very unintelligible. + Though I remade the code to use leaky bucket logic, which + is the most transparent one. Anyway, contribution by Eric is + the most important one since the previous release. + Short theory of operation: option -i (interval) sets rate r=1/interval pps, + option -l (preload) sets burst size of l packets. So, ping sends + at most r*t+l packets for an arbitrary interval t. + Default values: l=1 and for non-flood case: r=1pps, for flood r=infinity. + Nice? Exact algorithm is: + + Let N(t) be l/r=l*i initially and N(t) grow continuously with time as: + + N(t+delta) = min{l*i, N(t) + delta} + + Packet can be transmitted only at the time t_* when 1/r=i <= N(t_*) + and in this case N(t) jumps: + + N(t_* + 0) = N(t_* - 0) - i. + + When interval is zero, algo degenerates allowing to send any amount + of messages. In this case we modify it using l as limit on amount + of unanswered requests and waiting for 10msec, when something is not + answered. Note that the last thing (10msec) is just to be compatible with + BSD manual pages. BSD ping is simply not able to avoid delay technically, + we are able now. + + In result we got some new facilities: + * "-f -l 100" becomes very aggressive, in fact on good link + it holds permanently 100 packets in flight, which is very different + of earlier bevaviour (one packet in flight). + * -f and -i are not incompatible more. In fact, "-f -i 1" is equivalent + to plain ping, only output is different (dotted). Essentially, + change of output format is the only effect. "ping -i 0" is flood + printing output in normal format. + + Moved some parts of code to ping_common.c. Common part is not fully + trivial now. :-) + +* ping: Ian Lynagh , larger and dynamic dup detector. + Also, Ian submitted two large patches, one fixing formatting, another + doing something with signedness/longness. Not now... + Later note: found not working. x + 7 / 8 :-). Sorry... dubious, withdrawn. + size of table increased to maximal value instead (8K of memory, + not a big deal). +* tftpd: an old misprint. left@sbor.spb.su (Igor A. Lefterov) +* clockdiff: do not fail, if reversed resolution failed. + Tommy Lacroix +* ping: audible ping by Patrik Schilt + Patrick's option renamed to -a to align to freebsd. +* ping: react to device queue overflows using IP_RECVERR. me. +* ping: option -S allows to change sndbuf +* rarpd is moved from separate package here (people asked) +* ping6: kernel style bitops are not used more. +* Option -A to adapt to network rtt. +* Use BPF, when multiple pings are detected. + +[001110] +* ping is able to select TOS. By Pekka Savola +* tracepath* DNS names. By Pawel Krawczyk and + Arkadiusz Miskiewicz +* ping6 is expected to be compiled with linux-2.2. + +[001011] +* RH bugid#16677: segfault, when ping is used by root and size + is large enough. Fix is to allow oversize by root (it is necessary + to check kernel side), but clamp it at some safe value. + +[001010] +* More bug fixes from Chris Evans + - do not trust h_length returned by system resolver. + This value is meaningless in any case. + - ping: buffer overflow in fill()!!! Disgraceful bug. + +* ping: allow not-priviledged users to use broadcasts. It was paranoia. + Multicasts were allowed. 8) +* ping: but force broadcasts&multicasts not to fragment. BSD does + not allow to do this to anyone, we still allow this for superuser. +* Option -M to control path mtu discovery. + +[001007] +* By Pekka Savola + - SIOCGSTAMP/SO_TIMESTAMP are sensitive to bug in kernel. + When get_fast_time != gettimeofday (f.e. timestampless x86), + returned stamp can be out of sync with gettimeofday. + Workaround is not to use SIOCGSTAMP/SO_TIMESTAMP on such systems. + - fixes in man pages + - compiles under rh-7.0 +* Chris Evans + - ping: possible buffer overflow in pr_addr(). + +[000928] +* Sorry. I have lost all the CVS with changes made since 000418. + If someone sent me a patch after this date, please, resubmit. + Restored from the last backup and mailboxes: + +* ping*, SO_TIMESTAMP support. +* ping*, allow zero data length (reported by Damjan Lango ) +* iputils man and help updates. Pekka Savola +* ping.8, fix to ping man page. By Dadid Eisner +* ping prints addresses in numeric, if destination is numeric. + Proposed by Tim Waugh + +New: +* ping: strncpy bug +* arping: improvements by Charles Howes + - a feature to arping: quit as soon as a reply is received. + - default to eth0. + - spelling + +[000418] +* llsqrt() was buggy again! + (noticed by Sam Farin ) + +[000404] +* tracepath*, "NURDUnet-gw" bug workaround. + (noticed by Vitaly E.Lavrov ) +* tracepath*, handle case of routers initializing rtt to 128. + Vitaly E.Lavrov +* shadowed icmp_sock in ping6. James Morris +* Bug in ping -f, introduced with SO_RCVTIMEO. me. +* llsqrt() (ping, ping6) was wrong yet. me. + +[000310] +* Print mean deviation of RTT in ping/ping6. +* Use SIOCGSTAMP in ping/ping6. Old behaviour calculating + true user-to-user latency is restored with option -U. + Reason for this stupid change is mainly political; people + wonder why freebsd has twice less latency on loopback. + If to follow along this line, we have to print rtt equal to 0. 8) + [ LATER NOTE: actually, the change is _right_ without any doubts. + Ping has another bug: nameresolver is blocking, so that + when it dies not respond, ping shows evenly increasing by 1 sec + RTT. It is very confusing (look through linux-kernel maillists + to count number of people, who were cheated by misconfigured dns). ] +* Use SO_RCVTIMEO instead of poll() with ping/ping6 -f. +* Added -V option to arping/ping/ping6/traceroute6/rdisc + to print snapshot number. + +[000305] +* rdisc: ugly bug in getting interface list. me. +* ping/ping6: ping -i N, N>=3 did not work. Jeff Jonson +* ping/ping6: microsecond rtt measurements. me. + +[000120] +* ping/ping6: non-zero exit code even without -w. + +[991024] +* Option "-i" to ping/ping6 takes fractional time now, so that + "ping -i 0.3 xxx" pings each 300 msec. The idea is by + Marc Boucher +* alpha/glibc-2.1 alignment problems in ping are fixed (struct timeval + was wrongly aligned). + +[990915] +* ping/ping6 worked only with kernels 2.3.15+ in 990824. + +[990824] +* tftpd is added. It uses MSG_CONFIRM to confirm arp entries. +* ping6: workaround for bug in some egcs versions. + +[990610] +* ping: output buffer was too small for full sized ping. +* ping: silly restriction on ping size is removed. + +[990530] +* short man pages (Oleg M. Shumsky ) +* ping6: get and print hop limit of reply packets (ME) +* rdisc deletes routes before exit with -TERM +* ping/ping6: option -w TIMEOUT +* arping: exit with error, if received no replies in normal + (not DAD and not unsilicited ARP) mode. + diff --git a/SNAPSHOT.h b/SNAPSHOT.h new file mode 100644 index 0000000..cf241d9 --- /dev/null +++ b/SNAPSHOT.h @@ -0,0 +1 @@ +#define SNAPSHOT "s20180629" diff --git a/arping.c b/arping.c new file mode 100644 index 0000000..c2f2129 --- /dev/null +++ b/arping.c @@ -0,0 +1,1228 @@ +/* + * arping.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. + * + * Authors: Alexey Kuznetsov, + * YOSHIFUJI Hideaki + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CAPABILITIES +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef USE_SYSFS +#include +#include +#endif + +#ifndef WITHOUT_IFADDRS +#include +#endif + +#ifdef USE_IDN +#include +#endif + +#include "SNAPSHOT.h" + +static void usage(void) __attribute__((noreturn)); + +#ifdef DEFAULT_DEVICE +# define DEFAULT_DEVICE_STR DEFAULT_DEVICE +#else +# define DEFAULT_DEVICE NULL +#endif + +struct device { + char *name; + int ifindex; +#ifndef WITHOUT_IFADDRS + struct ifaddrs *ifa; +#endif +#ifdef USE_SYSFS + struct sysfs_devattr_values *sysfs; +#endif +}; + +int quit_on_reply=0; +struct device device = { + .name = DEFAULT_DEVICE, +}; +char *source; +struct in_addr src, dst; +char *target; +int dad, unsolicited, advert; +int quiet; +int count=-1; +int timeout; +int unicasting; +int s; +int broadcast_only; + +struct sockaddr_storage me; +struct sockaddr_storage he; + +struct timespec start, last; + +int sent, brd_sent; +int received, brd_recv, req_recv; + +#ifndef CAPABILITIES +static uid_t euid; +#endif + +#define MS_TDIFF(tv1,tv2) ( ((tv1).tv_sec-(tv2).tv_sec)*1000 + \ + ((tv1).tv_usec-(tv2).tv_usec)/1000 ) + +#define OFFSET_OF(name,ele) ((size_t)(((name *)0)->ele)) + +static inline socklen_t sll_len(size_t halen) +{ + socklen_t len = OFFSET_OF(struct sockaddr_ll, sll_addr) + halen; + if (len < sizeof(struct sockaddr_ll)) + len = sizeof(struct sockaddr_ll); + return len; +} + +#define SLL_LEN(hln) sll_len(hln) + +void usage(void) +{ + fprintf(stderr, + "Usage: arping [-fqbDUAV] [-c count] [-w timeout] [-I device] [-s source] destination\n" + " -f : quit on first reply\n" + " -q : be quiet\n" + " -b : keep broadcasting, don't go unicast\n" + " -D : duplicate address detection mode\n" + " -U : Unsolicited ARP mode, update your neighbours\n" + " -A : ARP answer mode, update your neighbours\n" + " -V : print version and exit\n" + " -c count : how many packets to send\n" + " -w timeout : how long to wait for a reply\n" + " -I device : which ethernet device to use" +#ifdef DEFAULT_DEVICE_STR + " (" DEFAULT_DEVICE_STR ")" +#endif + "\n" + " -s source : source ip address\n" + " destination : ask for what ip address\n" + ); + exit(2); +} + +void set_signal(int signo, void (*handler)(void)) +{ + struct sigaction sa; + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = (void (*)(int))handler; + sa.sa_flags = SA_RESTART; + sigaction(signo, &sa, NULL); +} + +#ifdef CAPABILITIES +static const cap_value_t caps[] = { CAP_NET_RAW, }; +static cap_flag_value_t cap_raw = CAP_CLEAR; +#endif + +void limit_capabilities(void) +{ +#ifdef CAPABILITIES + cap_t cap_p; + + cap_p = cap_get_proc(); + if (!cap_p) { + perror("arping: cap_get_proc"); + exit(-1); + } + + cap_get_flag(cap_p, CAP_NET_RAW, CAP_PERMITTED, &cap_raw); + + if (cap_raw != CAP_CLEAR) { + if (cap_clear(cap_p) < 0) { + perror("arping: cap_clear"); + exit(-1); + } + + cap_set_flag(cap_p, CAP_PERMITTED, 1, caps, CAP_SET); + + if (cap_set_proc(cap_p) < 0) { + perror("arping: cap_set_proc"); + if (errno != EPERM) + exit(-1); + } + } + + if (prctl(PR_SET_KEEPCAPS, 1) < 0) { + perror("arping: prctl"); + exit(-1); + } + + if (setuid(getuid()) < 0) { + perror("arping: setuid"); + exit(-1); + } + + if (prctl(PR_SET_KEEPCAPS, 0) < 0) { + perror("arping: prctl"); + exit(-1); + } + + cap_free(cap_p); +#else + euid = geteuid(); +#endif +} + +int modify_capability_raw(int on) +{ +#ifdef CAPABILITIES + cap_t cap_p; + + if (cap_raw != CAP_SET) + return on ? -1 : 0; + + cap_p = cap_get_proc(); + if (!cap_p) { + perror("arping: cap_get_proc"); + return -1; + } + + cap_set_flag(cap_p, CAP_EFFECTIVE, 1, caps, on ? CAP_SET : CAP_CLEAR); + + if (cap_set_proc(cap_p) < 0) { + perror("arping: cap_set_proc"); + return -1; + } + + cap_free(cap_p); +#else + if (setuid(on ? euid : getuid())) { + perror("arping: setuid"); + return -1; + } +#endif + return 0; +} + +static inline int enable_capability_raw(void) +{ + return modify_capability_raw(1); +} + +static inline int disable_capability_raw(void) +{ + return modify_capability_raw(0); +} + +void drop_capabilities(void) +{ +#ifdef CAPABILITIES + cap_t cap_p = cap_init(); + + if (!cap_p) { + perror("arping: cap_init"); + exit(-1); + } + + if (cap_set_proc(cap_p) < 0) { + perror("arping: cap_set_proc"); + exit(-1); + } + + cap_free(cap_p); +#else + if (setuid(getuid()) < 0) { + perror("arping: setuid"); + exit(-1); + } +#endif +} + +int send_pack(int s, struct in_addr src, struct in_addr dst, + struct sockaddr_ll *ME, struct sockaddr_ll *HE) +{ + int err; + struct timespec now; + unsigned char buf[256]; + struct arphdr *ah = (struct arphdr*)buf; + unsigned char *p = (unsigned char *)(ah+1); + + ah->ar_hrd = htons(ME->sll_hatype); + if (ah->ar_hrd == htons(ARPHRD_FDDI)) + ah->ar_hrd = htons(ARPHRD_ETHER); + ah->ar_pro = htons(ETH_P_IP); + ah->ar_hln = ME->sll_halen; + ah->ar_pln = 4; + ah->ar_op = advert ? htons(ARPOP_REPLY) : htons(ARPOP_REQUEST); + + memcpy(p, &ME->sll_addr, ah->ar_hln); + p+=ME->sll_halen; + + memcpy(p, &src, 4); + p+=4; + + if (advert) + memcpy(p, &ME->sll_addr, ah->ar_hln); + else + memcpy(p, &HE->sll_addr, ah->ar_hln); + p+=ah->ar_hln; + + memcpy(p, &dst, 4); + p+=4; + + clock_gettime(CLOCK_MONOTONIC, &now); + err = sendto(s, buf, p-buf, 0, (struct sockaddr*)HE, SLL_LEN(ah->ar_hln)); + if (err == p-buf) { + last = now; + sent++; + if (!unicasting) + brd_sent++; + } + return err; +} + +void finish(void) +{ + if (!quiet) { + printf("Sent %d probes (%d broadcast(s))\n", sent, brd_sent); + printf("Received %d response(s)", received); + if (brd_recv || req_recv) { + printf(" ("); + if (req_recv) + printf("%d request(s)", req_recv); + if (brd_recv) + printf("%s%d broadcast(s)", + req_recv ? ", " : "", + brd_recv); + printf(")"); + } + printf("\n"); + fflush(stdout); + } + if (dad) + exit(!!received); + if (unsolicited) + exit(0); + exit(!received); +} + +static void timespec_sub(struct timespec *a, struct timespec *b, + struct timespec *res) +{ + res->tv_sec = a->tv_sec - b->tv_sec; + res->tv_nsec = a->tv_nsec - b->tv_nsec; + if (a->tv_nsec < b->tv_nsec) { + res->tv_sec--; + res->tv_nsec += 1000000000; + } +} + +static int timespec_later(struct timespec *a, struct timespec *b) +{ + return (a->tv_sec > b->tv_sec) || + ((a->tv_sec == b->tv_sec) && (a->tv_nsec > b->tv_nsec)); +} + +void catcher(void) +{ + struct timespec ts, ts_s, ts_o; + + clock_gettime(CLOCK_MONOTONIC, &ts); + + if (start.tv_sec==0) + start = ts; + + timespec_sub(&ts, &start, &ts_s); + ts_o.tv_sec = timeout; + ts_o.tv_nsec = 500 * 1000000; + + if (timeout && timespec_later(&ts_s, &ts_o)) + finish(); + + timespec_sub(&ts, &last, &ts_s); + ts_o.tv_sec = 0; + + if (last.tv_sec==0 || timespec_later(&ts_s, &ts_o)) { + if (!timeout && (sent == count)) + finish(); + send_pack(s, src, dst, + (struct sockaddr_ll *)&me, (struct sockaddr_ll *)&he); + if ((sent == count) && unsolicited) + /* We usually wait for an extra iteration + * after sending the last request to see if we + * get a reply, but we don't need to in + * unsolicited mode */ + finish(); + } + alarm(1); +} + +void print_hex(unsigned char *p, int len) +{ + int i; + for (i=0; isll_pkttype != PACKET_HOST && + FROM->sll_pkttype != PACKET_BROADCAST && + FROM->sll_pkttype != PACKET_MULTICAST) + return 0; + + /* Only these types are recognised */ + if (ah->ar_op != htons(ARPOP_REQUEST) && + ah->ar_op != htons(ARPOP_REPLY)) + return 0; + + /* ARPHRD check and this darned FDDI hack here :-( */ + if (ah->ar_hrd != htons(FROM->sll_hatype) && + (FROM->sll_hatype != ARPHRD_FDDI || ah->ar_hrd != htons(ARPHRD_ETHER))) + return 0; + + /* Protocol must be IP. */ + if (ah->ar_pro != htons(ETH_P_IP)) + return 0; + if (ah->ar_pln != 4) + return 0; + if (ah->ar_hln != ((struct sockaddr_ll *)&me)->sll_halen) + return 0; + if (len < sizeof(*ah) + 2*(4 + ah->ar_hln)) + return 0; + memcpy(&src_ip, p+ah->ar_hln, 4); + memcpy(&dst_ip, p+ah->ar_hln+4+ah->ar_hln, 4); + if (!dad) { + if (src_ip.s_addr != dst.s_addr) + return 0; + if (src.s_addr != dst_ip.s_addr) + return 0; + if (memcmp(p+ah->ar_hln+4, ((struct sockaddr_ll *)&me)->sll_addr, ah->ar_hln)) + return 0; + } else { + /* DAD packet was: + src_ip = 0 (or some src) + src_hw = ME + dst_ip = tested address + dst_hw = + + We fail, if receive request/reply with: + src_ip = tested_address + src_hw != ME + if src_ip in request was not zero, check + also that it matches to dst_ip, otherwise + dst_ip/dst_hw do not matter. + */ + if (src_ip.s_addr != dst.s_addr) + return 0; + if (memcmp(p, ((struct sockaddr_ll *)&me)->sll_addr, ((struct sockaddr_ll *)&me)->sll_halen) == 0) + return 0; + if (src.s_addr && src.s_addr != dst_ip.s_addr) + return 0; + } + if (!quiet) { + int s_printed = 0; + printf("%s ", FROM->sll_pkttype==PACKET_HOST ? "Unicast" : "Broadcast"); + printf("%s from ", ah->ar_op == htons(ARPOP_REPLY) ? "reply" : "request"); + printf("%s [", inet_ntoa(src_ip)); + print_hex(p, ah->ar_hln); + printf("] "); + if (dst_ip.s_addr != src.s_addr) { + printf("for %s ", inet_ntoa(dst_ip)); + s_printed = 1; + } + if (memcmp(p+ah->ar_hln+4, ((struct sockaddr_ll *)&me)->sll_addr, ah->ar_hln)) { + if (!s_printed) + printf("for "); + printf("["); + print_hex(p+ah->ar_hln+4, ah->ar_hln); + printf("]"); + } + if (last.tv_sec) { + long usecs = (ts.tv_sec-last.tv_sec) * 1000000 + + (ts.tv_nsec-last.tv_nsec+500) / 1000; + long msecs = (usecs+500)/1000; + usecs -= msecs*1000 - 500; + printf(" %ld.%03ldms\n", msecs, usecs); + } else { + printf(" UNSOLICITED?\n"); + } + fflush(stdout); + } + received++; + if (timeout && (received == count)) + finish(); + if (FROM->sll_pkttype != PACKET_HOST) + brd_recv++; + if (ah->ar_op == htons(ARPOP_REQUEST)) + req_recv++; + if (quit_on_reply || (count == 0 && received == sent)) + finish(); + if(!broadcast_only) { + memcpy(((struct sockaddr_ll *)&he)->sll_addr, p, ((struct sockaddr_ll *)&me)->sll_halen); + unicasting=1; + } + return 1; +} + +#ifdef USE_SYSFS +union sysfs_devattr_value { + unsigned long ulong; + void *ptr; +}; + +enum { + SYSFS_DEVATTR_IFINDEX, + SYSFS_DEVATTR_FLAGS, + SYSFS_DEVATTR_ADDR_LEN, + SYSFS_DEVATTR_BROADCAST, + SYSFS_DEVATTR_NUM +}; + +struct sysfs_devattr_values +{ + char *ifname; + union sysfs_devattr_value value[SYSFS_DEVATTR_NUM]; +}; + +static int sysfs_devattr_ulong_dec(char *ptr, struct sysfs_devattr_values *v, unsigned idx); +static int sysfs_devattr_ulong_hex(char *ptr, struct sysfs_devattr_values *v, unsigned idx); +static int sysfs_devattr_macaddr(char *ptr, struct sysfs_devattr_values *v, unsigned idx); + +struct sysfs_devattrs { + const char *name; + int (*handler)(char *ptr, struct sysfs_devattr_values *v, unsigned int idx); + int free; +} sysfs_devattrs[SYSFS_DEVATTR_NUM] = { + [SYSFS_DEVATTR_IFINDEX] = { + .name = "ifindex", + .handler = sysfs_devattr_ulong_dec, + }, + [SYSFS_DEVATTR_ADDR_LEN] = { + .name = "addr_len", + .handler = sysfs_devattr_ulong_dec, + }, + [SYSFS_DEVATTR_FLAGS] = { + .name = "flags", + .handler = sysfs_devattr_ulong_hex, + }, + [SYSFS_DEVATTR_BROADCAST] = { + .name = "broadcast", + .handler = sysfs_devattr_macaddr, + .free = 1, + }, +}; +#endif + +/* + * find_device() + * + * This function checks 1) if the device (if given) is okay for ARP, + * or 2) find fist appropriate device on the system. + * + * Return value: + * >0 : Succeeded, and appropriate device not found. + * device.ifindex remains 0. + * 0 : Succeeded, and approptiate device found. + * device.ifindex is set. + * <0 : Failed. Support not found, or other + * : system error. Try other method. + * + * If an appropriate device found, it is recorded inside the + * "device" variable for later reference. + * + * We have several implementations for this. + * by_ifaddrs(): requires getifaddr() in glibc, and rtnetlink in + * kernel. default and recommended for recent systems. + * by_sysfs(): requires libsysfs , and sysfs in kernel. + * by_ioctl(): unable to list devices without ipv4 address; this + * means, you need to supply the device name for + * DAD purpose. + */ +/* Common check for ifa->ifa_flags */ +static int check_ifflags(unsigned int ifflags, int fatal) +{ + if (!(ifflags & IFF_UP)) { + if (fatal) { + if (!quiet) + printf("Interface \"%s\" is down\n", device.name); + exit(2); + } + return -1; + } + if (ifflags & (IFF_NOARP | IFF_LOOPBACK)) { + if (fatal) { + if (!quiet) + printf("Interface \"%s\" is not ARPable\n", device.name); + exit(dad ? 0 : 2); + } + return -1; + } + return 0; +} + +static int find_device_by_ifaddrs(void) +{ +#ifndef WITHOUT_IFADDRS + int rc; + struct ifaddrs *ifa0, *ifa; + int n = 0; + + rc = getifaddrs(&ifa0); + if (rc) { + perror("getifaddrs"); + return -1; + } + + for (ifa = ifa0; ifa; ifa = ifa->ifa_next) { + if (!ifa->ifa_addr) + continue; + if (ifa->ifa_addr->sa_family != AF_PACKET) + continue; + if (device.name && ifa->ifa_name && strcmp(ifa->ifa_name, device.name)) + continue; + + if (check_ifflags(ifa->ifa_flags, device.name != NULL) < 0) + continue; + + if (!((struct sockaddr_ll *)ifa->ifa_addr)->sll_halen) + continue; + if (!ifa->ifa_broadaddr) + continue; + + device.ifa = ifa; + + if (n++) + break; + } + + if (n == 1 && device.ifa) { + device.ifindex = if_nametoindex(device.ifa->ifa_name); + if (!device.ifindex) { + perror("arping: if_nametoindex"); + freeifaddrs(ifa0); + return -1; + } + device.name = device.ifa->ifa_name; + return 0; + } + return 1; +#else + return -1; +#endif +} + +#ifdef USE_SYSFS +static void sysfs_devattr_values_init(struct sysfs_devattr_values *v, int do_free) +{ + int i; + if (do_free) { + free(v->ifname); + for (i = 0; i < SYSFS_DEVATTR_NUM; i++) { + if (sysfs_devattrs[i].free) + free(v->value[i].ptr); + } + } + memset(v, 0, sizeof(*v)); +} + +static int sysfs_devattr_ulong(char *ptr, struct sysfs_devattr_values *v, unsigned int idx, + unsigned int base) +{ + unsigned long *p; + char *ep; + + if (!ptr || !v) + return -1; + + p = &v->value[idx].ulong; + errno = 0; + *p = strtoul(ptr, &ep, base); + if ((*ptr && isspace(*ptr & 0xff)) || errno || (*ep != '\0' && *ep != '\n')) + goto out; + + return 0; +out: + return -1; +} + +static int sysfs_devattr_ulong_dec(char *ptr, struct sysfs_devattr_values *v, unsigned int idx) +{ + int rc = sysfs_devattr_ulong(ptr, v, idx, 10); + return rc; +} + +static int sysfs_devattr_ulong_hex(char *ptr, struct sysfs_devattr_values *v, unsigned int idx) +{ + int rc = sysfs_devattr_ulong(ptr, v, idx, 16); + return rc; +} + +static int sysfs_devattr_macaddr(char *ptr, struct sysfs_devattr_values *v, unsigned int idx) +{ + unsigned char *m; + int i; + unsigned int addrlen; + + if (!ptr || !v) + return -1; + + addrlen = v->value[SYSFS_DEVATTR_ADDR_LEN].ulong; + m = malloc(addrlen); + + for (i = 0; i < addrlen; i++) { + if (i && *(ptr + i * 3 - 1) != ':') + goto out; + if (sscanf(ptr + i * 3, "%02hhx", &m[i]) != 1) + goto out; + } + + v->value[idx].ptr = m; + return 0; +out: + free(m); + return -1; +} +#endif + +int find_device_by_sysfs(void) +{ + int rc = -1; +#ifdef USE_SYSFS + DIR *dir; + struct dirent *dirp; + struct sysfs_devattr_values sysfs_devattr_values; + int n = 0; + + if (!device.sysfs) { + device.sysfs = malloc(sizeof(*device.sysfs)); + sysfs_devattr_values_init(device.sysfs, 0); + } + dir = opendir("/sys/class/net"); + + sysfs_devattr_values_init(&sysfs_devattr_values, 0); + + while ((dirp = readdir(dir)) != NULL) { + int i; + int rc = -1; + + if (!strcmp(dirp->d_name, ".") || !strcmp(dirp->d_name, "..")) + continue; + if (device.name && strcmp(dirp->d_name, device.name)) + goto do_next; + + sysfs_devattr_values_init(&sysfs_devattr_values, 1); + + for (i = 0; i < SYSFS_DEVATTR_NUM; i++) { + char path[PATH_MAX]; + char str[256]; + FILE *f; + + sprintf(path, "/sys/class/net/%s/%s", dirp->d_name, sysfs_devattrs[i].name); + f = fopen(path, "r"); + if (!f) + continue; + if (fscanf(f, "%255s", str) != 1) + str[0] = '\0'; + fclose(f); + rc = sysfs_devattrs[i].handler(str, &sysfs_devattr_values, i); + + if (rc < 0) + break; + } + + if (rc < 0) + goto do_next; + + if (check_ifflags(sysfs_devattr_values.value[SYSFS_DEVATTR_FLAGS].ulong, + device.name != NULL) < 0) + goto do_next; + + if (!sysfs_devattr_values.value[SYSFS_DEVATTR_ADDR_LEN].ulong) + goto do_next; + + if (device.sysfs->value[SYSFS_DEVATTR_IFINDEX].ulong) { + if (device.sysfs->value[SYSFS_DEVATTR_FLAGS].ulong & IFF_RUNNING) + goto do_next; + } + + sysfs_devattr_values.ifname = strdup(dirp->d_name); + if (!sysfs_devattr_values.ifname) { + perror("malloc"); + goto out; + } + + sysfs_devattr_values_init(device.sysfs, 1); + memcpy(device.sysfs, &sysfs_devattr_values, sizeof(*device.sysfs)); + sysfs_devattr_values_init(&sysfs_devattr_values, 0); + + if (n++) + break; + + continue; +do_next: + sysfs_devattr_values_init(&sysfs_devattr_values, 1); + } + + if (n == 1) { + device.ifindex = device.sysfs->value[SYSFS_DEVATTR_IFINDEX].ulong; + device.name = device.sysfs->ifname; + } + rc = !device.ifindex; +out: + closedir(dir); +#endif + return rc; +} + +static int check_device_by_ioctl(int s, struct ifreq *ifr) +{ + if (ioctl(s, SIOCGIFFLAGS, ifr) < 0) { + perror("ioctl(SIOCGIFINDEX"); + return -1; + } + + if (check_ifflags(ifr->ifr_flags, device.name != NULL) < 0) + return 1; + + if (ioctl(s, SIOCGIFINDEX, ifr) < 0) { + perror("ioctl(SIOCGIFINDEX"); + return -1; + } + + return 0; +} + +static int find_device_by_ioctl(void) +{ + int s; + struct ifreq *ifr0, *ifr, *ifr_end; + size_t ifrsize = sizeof(*ifr); + struct ifconf ifc; + static struct ifreq ifrbuf; + int n = 0; + + s = socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket"); + return -1; + } + + memset(&ifrbuf, 0, sizeof(ifrbuf)); + + if (device.name) { + strncpy(ifrbuf.ifr_name, device.name, sizeof(ifrbuf.ifr_name) - 1); + if (check_device_by_ioctl(s, &ifrbuf)) + goto out; + n++; + } else { + do { + int rc; + ifr0 = malloc(ifrsize); + if (!ifr0) { + perror("malloc"); + goto out; + } + + ifc.ifc_buf = (char *)ifr0; + ifc.ifc_len = ifrsize; + + rc = ioctl(s, SIOCGIFCONF, &ifc); + if (rc < 0) { + perror("ioctl(SIOCFIFCONF"); + goto out; + } + + if (ifc.ifc_len + sizeof(*ifr0) + sizeof(struct sockaddr_storage) - sizeof(struct sockaddr) <= ifrsize) + break; + ifrsize *= 2; + free(ifr0); + ifr0 = NULL; + } while(ifrsize < INT_MAX / 2); + + if (!ifr0) { + fprintf(stderr, "arping: too many interfaces!?\n"); + goto out; + } + + ifr_end = (struct ifreq *)(((char *)ifr0) + ifc.ifc_len - sizeof(*ifr0)); + for (ifr = ifr0; ifr <= ifr_end; ifr++) { + if (check_device_by_ioctl(s, &ifrbuf)) + continue; + memcpy(&ifrbuf.ifr_name, ifr->ifr_name, sizeof(ifrbuf.ifr_name)); + if (n++) + break; + } + } + + close(s); + + if (n == 1) { + device.ifindex = ifrbuf.ifr_ifindex; + device.name = ifrbuf.ifr_name; + } + return !device.ifindex; +out: + close(s); + return -1; +} + +static int find_device(void) +{ + int rc; + rc = find_device_by_ifaddrs(); + if (rc >= 0) + goto out; + rc = find_device_by_sysfs(); + if (rc >= 0) + goto out; + rc = find_device_by_ioctl(); +out: + return rc; +} + +/* + * set_device_broadcast() + * + * This fills the device "broadcast address" + * based on information found by find_device() funcion. + */ +static int set_device_broadcast_ifaddrs_one(struct device *device, unsigned char *ba, size_t balen, int fatal) +{ +#ifndef WITHOUT_IFADDRS + struct ifaddrs *ifa; + struct sockaddr_ll *sll; + + if (!device) + return -1; + + ifa = device->ifa; + if (!ifa) + return -1; + + sll = (struct sockaddr_ll *)ifa->ifa_broadaddr; + + if (sll->sll_halen != balen) { + if (fatal) { + if (!quiet) + printf("Address length does not match...\n"); + exit(2); + } + return -1; + } + memcpy(ba, sll->sll_addr, sll->sll_halen); + return 0; +#else + return -1; +#endif +} +int set_device_broadcast_sysfs(struct device *device, unsigned char *ba, size_t balen) +{ +#ifdef USE_SYSFS + struct sysfs_devattr_values *v; + if (!device) + return -1; + v = device->sysfs; + if (!v) + return -1; + if (v->value[SYSFS_DEVATTR_ADDR_LEN].ulong != balen) + return -1; + memcpy(ba, v->value[SYSFS_DEVATTR_BROADCAST].ptr, balen); + return 0; +#else + return -1; +#endif +} + +static int set_device_broadcast_fallback(struct device *device, unsigned char *ba, size_t balen) +{ + if (!quiet) + fprintf(stderr, "WARNING: using default broadcast address.\n"); + memset(ba, -1, balen); + return 0; +} + +static void set_device_broadcast(struct device *dev, unsigned char *ba, size_t balen) +{ + if (!set_device_broadcast_ifaddrs_one(dev, ba, balen, 0)) + return; + if (!set_device_broadcast_sysfs(dev, ba, balen)) + return; + set_device_broadcast_fallback(dev, ba, balen); +} + +int +main(int argc, char **argv) +{ + int socket_errno; + int ch; + + limit_capabilities(); + +#ifdef USE_IDN + setlocale(LC_ALL, ""); +#endif + + enable_capability_raw(); + + s = socket(PF_PACKET, SOCK_DGRAM, 0); + socket_errno = errno; + + disable_capability_raw(); + + while ((ch = getopt(argc, argv, "h?bfDUAqc:w:s:I:V")) != EOF) { + switch(ch) { + case 'b': + broadcast_only=1; + break; + case 'D': + dad++; + quit_on_reply=1; + break; + case 'U': + unsolicited++; + break; + case 'A': + advert++; + unsolicited++; + break; + case 'q': + quiet++; + break; + case 'c': + count = atoi(optarg); + break; + case 'w': + timeout = atoi(optarg); + break; + case 'I': + device.name = optarg; + break; + case 'f': + quit_on_reply=1; + break; + case 's': + source = optarg; + break; + case 'V': + printf("arping utility, iputils-%s\n", SNAPSHOT); + exit(0); + case 'h': + case '?': + default: + usage(); + } + } + argc -= optind; + argv += optind; + + if (argc != 1) + usage(); + + target = *argv; + + if (device.name && !*device.name) + device.name = NULL; + + if (s < 0) { + errno = socket_errno; + perror("arping: socket"); + exit(2); + } + + if (find_device() < 0) + exit(2); + + if (!device.ifindex) { + if (device.name) { + fprintf(stderr, "arping: Device %s not available.\n", device.name); + exit(2); + } + fprintf(stderr, "arping: Suitable device could not be determined. Please, use option -I.\n"); + usage(); + } + + if (inet_aton(target, &dst) != 1) { + struct addrinfo hints = { + .ai_family = AF_INET, + .ai_socktype = SOCK_RAW, +#ifdef USE_IDN + .ai_flags = AI_IDN | AI_CANONIDN +#endif + }; + struct addrinfo *result; + int status; + + status = getaddrinfo(target, NULL, &hints, &result); + if (status) { + fprintf(stderr, "arping: %s: %s\n", target, gai_strerror(status)); + exit(2); + } + + memcpy(&dst, &((struct sockaddr_in *) result->ai_addr)->sin_addr, sizeof dst); + freeaddrinfo(result); + } + + if (source && inet_aton(source, &src) != 1) { + fprintf(stderr, "arping: invalid source %s\n", source); + exit(2); + } + + if (!dad && unsolicited && src.s_addr == 0) + src = dst; + + if (!dad || src.s_addr) { + struct sockaddr_in saddr; + int probe_fd = socket(AF_INET, SOCK_DGRAM, 0); + + if (probe_fd < 0) { + perror("socket"); + exit(2); + } + if (device.name) { + enable_capability_raw(); + + if (setsockopt(probe_fd, SOL_SOCKET, SO_BINDTODEVICE, device.name, strlen(device.name)+1) == -1) + perror("WARNING: interface is ignored"); + + disable_capability_raw(); + } + memset(&saddr, 0, sizeof(saddr)); + saddr.sin_family = AF_INET; + if (src.s_addr) { + saddr.sin_addr = src; + if (bind(probe_fd, (struct sockaddr*)&saddr, sizeof(saddr)) == -1) { + perror("bind"); + exit(2); + } + } else if (!dad) { + int on = 1; + socklen_t alen = sizeof(saddr); + + saddr.sin_port = htons(1025); + saddr.sin_addr = dst; + + if (setsockopt(probe_fd, SOL_SOCKET, SO_DONTROUTE, (char*)&on, sizeof(on)) == -1) + perror("WARNING: setsockopt(SO_DONTROUTE)"); + if (connect(probe_fd, (struct sockaddr*)&saddr, sizeof(saddr)) == -1) { + perror("connect"); + exit(2); + } + if (getsockname(probe_fd, (struct sockaddr*)&saddr, &alen) == -1) { + perror("getsockname"); + exit(2); + } + src = saddr.sin_addr; + } + close(probe_fd); + }; + + ((struct sockaddr_ll *)&me)->sll_family = AF_PACKET; + ((struct sockaddr_ll *)&me)->sll_ifindex = device.ifindex; + ((struct sockaddr_ll *)&me)->sll_protocol = htons(ETH_P_ARP); + if (bind(s, (struct sockaddr*)&me, sizeof(me)) == -1) { + perror("bind"); + exit(2); + } + + if (1) { + socklen_t alen = sizeof(me); + if (getsockname(s, (struct sockaddr*)&me, &alen) == -1) { + perror("getsockname"); + exit(2); + } + } + if (((struct sockaddr_ll *)&me)->sll_halen == 0) { + if (!quiet) + printf("Interface \"%s\" is not ARPable (no ll address)\n", device.name); + exit(dad?0:2); + } + + he = me; + + set_device_broadcast(&device, ((struct sockaddr_ll *)&he)->sll_addr, + ((struct sockaddr_ll *)&he)->sll_halen); + + if (!quiet) { + printf("ARPING %s ", inet_ntoa(dst)); + printf("from %s %s\n", inet_ntoa(src), device.name ? : ""); + } + + if (!src.s_addr && !dad) { + fprintf(stderr, "arping: no source address in not-DAD mode\n"); + exit(2); + } + + drop_capabilities(); + + set_signal(SIGINT, finish); + set_signal(SIGALRM, catcher); + + catcher(); + + while(1) { + sigset_t sset, osset; + unsigned char packet[4096]; + struct sockaddr_storage from; + socklen_t alen = sizeof(from); + int cc; + + sigemptyset(&sset); + sigaddset(&sset, SIGALRM); + sigaddset(&sset, SIGINT); + /* Unblock SIGALRM so that the previously called alarm() + * can prevent recvfrom from blocking forever in case the + * inherited procmask is blocking SIGALRM and no packet + * is received. */ + sigprocmask(SIG_UNBLOCK, &sset, &osset); + + if ((cc = recvfrom(s, packet, sizeof(packet), 0, + (struct sockaddr *)&from, &alen)) < 0) { + perror("arping: recvfrom"); + if (errno == ENETDOWN) + exit(2); + continue; + } + + sigprocmask(SIG_BLOCK, &sset, NULL); + recv_pack(packet, cc, (struct sockaddr_ll *)&from); + sigprocmask(SIG_SETMASK, &osset, NULL); + } +} + + diff --git a/builddir/.placeholder b/builddir/.placeholder new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/builddir/.placeholder diff --git a/clockdiff.c b/clockdiff.c new file mode 100644 index 0000000..d8d0974 --- /dev/null +++ b/clockdiff.c @@ -0,0 +1,715 @@ +/*- + * Copyright (c) 1985, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define TSPTYPES +#include +#include +#include +#include +#include +#ifdef CAPABILITIES +#include +#endif + +void usage(void) __attribute__((noreturn)); + +#define MAX_HOSTNAMELEN NI_MAXHOST + +/* + * Checksum routine for Internet Protocol family headers. + * + * This routine is very heavily used in the network + * code and should be modified for each CPU to be as fast as possible. + * + * This implementation is TAHOE version. + */ + +#undef ADDCARRY +#define ADDCARRY(sum) { \ + if (sum & 0xffff0000) { \ + sum &= 0xffff; \ + sum++; \ + } \ +} + +int in_cksum(unsigned short *addr, int len) +{ + union word { + char c[2]; + unsigned short s; + } u; + int sum = 0; + + while (len > 0) { + /* + * add by words. + */ + while ((len -= 2) >= 0) { + if ((unsigned long)addr & 0x1) { + /* word is not aligned */ + u.c[0] = *(char *)addr; + u.c[1] = *((char *)addr+1); + sum += u.s; + addr++; + } else + sum += *addr++; + ADDCARRY(sum); + } + if (len == -1) + /* + * Odd number of bytes. + */ + u.c[0] = *(unsigned char *)addr; + } + if (len == -1) { + /* The last mbuf has odd # of bytes. Follow the + standard (the odd byte is shifted left by 8 bits) */ + u.c[1] = 0; + sum += u.s; + ADDCARRY(sum); + } + return (~sum & 0xffff); +} + +#define ON 1 +#define OFF 0 + +#define RANGE 1 /* best expected round-trip time, ms */ +#define MSGS 50 +#define TRIALS 10 + +#define GOOD 0 +#define UNREACHABLE 2 +#define NONSTDTIME 3 +#define HOSTDOWN 0x7fffffff + + +int interactive = 0; +uint16_t id; +int sock; +int sock_raw; +struct sockaddr_in server; +int ip_opt_len = 0; + +#define BIASP 43199999 +#define BIASN -43200000 +#define MODULO 86400000 +#define PROCESSING_TIME 0 /* ms. to reduce error in measurement */ + +#define PACKET_IN 1024 + +int measure_delta; +int measure_delta1; +static unsigned short seqno, seqno0, acked; +long rtt = 1000; +long min_rtt; +long rtt_sigma = 0; + +/* + * Measures the differences between machines' clocks using + * ICMP timestamp messages. + */ +int +measure(struct sockaddr_in * addr) +{ + socklen_t length; + int msgcount; + int cc, count; + fd_set ready; + long sendtime, recvtime, histime; + long min1, min2, diff; + long delta1, delta2; + struct timeval tv1, tout; + unsigned char packet[PACKET_IN], opacket[64]; + struct icmphdr *icp; + struct icmphdr *oicp = (struct icmphdr *) opacket; + struct iphdr *ip = (struct iphdr *) packet; + + min1 = min2 = 0x7fffffff; + min_rtt = 0x7fffffff; + measure_delta = HOSTDOWN; + measure_delta1 = HOSTDOWN; + +/* empties the icmp input queue */ + FD_ZERO(&ready); + +empty: + tout.tv_sec = tout.tv_usec = 0; + FD_SET(sock_raw, &ready); + if (select(FD_SETSIZE, &ready, (fd_set *)0, (fd_set *)0, &tout)) { + length = sizeof(struct sockaddr_in); + cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0, + (struct sockaddr *)NULL, &length); + if (cc < 0) + return -1; + goto empty; + } + + /* + * To measure the difference, select MSGS messages whose round-trip + * time is smaller than RANGE if ckrange is 1, otherwise simply + * select MSGS messages regardless of round-trip transmission time. + * Choose the smallest transmission time in each of the two directions. + * Use these two latter quantities to compute the delta between + * the two clocks. + */ + + length = sizeof(struct sockaddr_in); + oicp->type = ICMP_TIMESTAMP; + oicp->code = 0; + oicp->checksum = 0; + oicp->un.echo.id = id; + ((__u32*)(oicp+1))[0] = 0; + ((__u32*)(oicp+1))[1] = 0; + ((__u32*)(oicp+1))[2] = 0; + FD_ZERO(&ready); + + acked = seqno = seqno0 = 0; + + for (msgcount = 0; msgcount < MSGS; ) { + + /* + * If no answer is received for TRIALS consecutive times, + * the machine is assumed to be down + */ + if (seqno - acked > TRIALS) + return HOSTDOWN; + + oicp->un.echo.sequence = ++seqno; + oicp->checksum = 0; + + (void)gettimeofday (&tv1, (struct timezone *)0); + *(__u32*)(oicp+1) = htonl((tv1.tv_sec % (24*60*60)) * 1000 + + tv1.tv_usec / 1000); + oicp->checksum = in_cksum((unsigned short *)oicp, sizeof(*oicp) + 12); + + count = sendto(sock_raw, (char *)opacket, sizeof(*oicp)+12, 0, + (struct sockaddr *)addr, sizeof(struct sockaddr_in)); + + if (count < 0) + return UNREACHABLE; + + for (;;) { + FD_ZERO(&ready); + FD_SET(sock_raw, &ready); + { + long tmo = rtt + rtt_sigma; + tout.tv_sec = tmo/1000; + tout.tv_usec = (tmo - (tmo/1000)*1000)*1000; + } + + if ((count = select(FD_SETSIZE, &ready, (fd_set *)0, + (fd_set *)0, &tout)) <= 0) + break; + + (void)gettimeofday(&tv1, (struct timezone *)0); + cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0, + (struct sockaddr *)NULL, &length); + + if (cc < 0) + return(-1); + + icp = (struct icmphdr *)(packet + (ip->ihl << 2)); + if( icp->type == ICMP_TIMESTAMPREPLY && + icp->un.echo.id == id && icp->un.echo.sequence >= seqno0 && + icp->un.echo.sequence <= seqno) { + if (acked < icp->un.echo.sequence) + acked = icp->un.echo.sequence; + + recvtime = (tv1.tv_sec % (24*60*60)) * 1000 + + tv1.tv_usec / 1000; + sendtime = ntohl(*(__u32*)(icp+1)); + diff = recvtime - sendtime; + /* + * diff can be less than 0 aroud midnight + */ + if (diff < 0) + continue; + rtt = (rtt * 3 + diff)/4; + rtt_sigma = (rtt_sigma *3 + abs(diff-rtt))/4; + msgcount++; + histime = ntohl(((__u32*)(icp+1))[1]); + /* + * a hosts using a time format different from + * ms. since midnight UT (as per RFC792) should + * set the high order bit of the 32-bit time + * value it transmits. + */ + if ((histime & 0x80000000) != 0) + return NONSTDTIME; + + if (interactive) { + printf("."); + fflush(stdout); + } + + delta1 = histime - sendtime; + /* + * Handles wrap-around to avoid that around + * midnight small time differences appear + * enormous. However, the two machine's clocks + * must be within 12 hours from each other. + */ + if (delta1 < BIASN) + delta1 += MODULO; + else if (delta1 > BIASP) + delta1 -= MODULO; + + delta2 = recvtime - histime; + if (delta2 < BIASN) + delta2 += MODULO; + else if (delta2 > BIASP) + delta2 -= MODULO; + + if (delta1 < min1) + min1 = delta1; + if (delta2 < min2) + min2 = delta2; + if (delta1 + delta2 < min_rtt) { + min_rtt = delta1 + delta2; + measure_delta1 = (delta1 - delta2)/2 + PROCESSING_TIME; + } + if (diff < RANGE) { + min1 = delta1; + min2 = delta2; + goto good_exit; + } + } + } + } + +good_exit: + measure_delta = (min1 - min2)/2 + PROCESSING_TIME; + return GOOD; +} + +char *myname, *hisname; + +int +measure_opt(struct sockaddr_in * addr) +{ + socklen_t length; + int msgcount; + int cc, count; + fd_set ready; + long sendtime, recvtime, histime, histime1; + long min1, min2, diff; + long delta1, delta2; + struct timeval tv1, tout; + unsigned char packet[PACKET_IN], opacket[64]; + struct icmphdr *icp; + struct icmphdr *oicp = (struct icmphdr *) opacket; + struct iphdr *ip = (struct iphdr *) packet; + + min1 = min2 = 0x7fffffff; + min_rtt = 0x7fffffff; + measure_delta = HOSTDOWN; + measure_delta1 = HOSTDOWN; + +/* empties the icmp input queue */ + FD_ZERO(&ready); +empty: + tout.tv_sec = tout.tv_usec = 0; + FD_SET(sock_raw, &ready); + if (select(FD_SETSIZE, &ready, (fd_set *)0, (fd_set *)0, &tout)) { + length = sizeof(struct sockaddr_in); + cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0, + (struct sockaddr *)NULL, &length); + if (cc < 0) + return -1; + goto empty; + } + + /* + * To measure the difference, select MSGS messages whose round-trip + * time is smaller than RANGE if ckrange is 1, otherwise simply + * select MSGS messages regardless of round-trip transmission time. + * Choose the smallest transmission time in each of the two directions. + * Use these two latter quantities to compute the delta between + * the two clocks. + */ + + length = sizeof(struct sockaddr_in); + oicp->type = ICMP_ECHO; + oicp->code = 0; + oicp->checksum = 0; + oicp->un.echo.id = id; + ((__u32*)(oicp+1))[0] = 0; + ((__u32*)(oicp+1))[1] = 0; + ((__u32*)(oicp+1))[2] = 0; + + FD_ZERO(&ready); + + acked = seqno = seqno0 = 0; + + for (msgcount = 0; msgcount < MSGS; ) { + + /* + * If no answer is received for TRIALS consecutive times, + * the machine is assumed to be down + */ + if ( seqno - acked > TRIALS) { + errno = EHOSTDOWN; + return HOSTDOWN; + } + oicp->un.echo.sequence = ++seqno; + oicp->checksum = 0; + + gettimeofday (&tv1, NULL); + ((__u32*)(oicp+1))[0] = htonl((tv1.tv_sec % (24*60*60)) * 1000 + + tv1.tv_usec / 1000); + oicp->checksum = in_cksum((unsigned short *)oicp, sizeof(*oicp)+12); + + count = sendto(sock_raw, (char *)opacket, sizeof(*oicp)+12, 0, + (struct sockaddr *)addr, sizeof(struct sockaddr_in)); + + if (count < 0) { + errno = EHOSTUNREACH; + return UNREACHABLE; + } + + for (;;) { + FD_ZERO(&ready); + FD_SET(sock_raw, &ready); + { + long tmo = rtt + rtt_sigma; + tout.tv_sec = tmo/1000; + tout.tv_usec = (tmo - (tmo/1000)*1000)*1000; + } + + if ((count = select(FD_SETSIZE, &ready, (fd_set *)0, + (fd_set *)0, &tout)) <= 0) + break; + + (void)gettimeofday(&tv1, (struct timezone *)0); + cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0, + (struct sockaddr *)NULL, &length); + + if (cc < 0) + return(-1); + + icp = (struct icmphdr *)(packet + (ip->ihl << 2)); + if (icp->type == ICMP_ECHOREPLY && + packet[20] == IPOPT_TIMESTAMP && + icp->un.echo.id == id && + icp->un.echo.sequence >= seqno0 && + icp->un.echo.sequence <= seqno) { + int i; + __u8 *opt = packet+20; + + if (acked < icp->un.echo.sequence) + acked = icp->un.echo.sequence; + if ((opt[3]&0xF) != IPOPT_TS_PRESPEC) { + fprintf(stderr, "Wrong timestamp %d\n", opt[3]&0xF); + return NONSTDTIME; + } + if (opt[3]>>4) { + if ((opt[3]>>4) != 1 || ip_opt_len != 4+3*8) + fprintf(stderr, "Overflow %d hops\n", opt[3]>>4); + } + sendtime = recvtime = histime = histime1 = 0; + for (i=0; i < (opt[2]-5)/8; i++) { + __u32 *timep = (__u32*)(opt+4+i*8+4); + __u32 t = ntohl(*timep); + + if (t & 0x80000000) + return NONSTDTIME; + + if (i == 0) + sendtime = t; + if (i == 1) + histime = histime1 = t; + if (i == 2) { + if (ip_opt_len == 4+4*8) + histime1 = t; + else + recvtime = t; + } + if (i == 3) + recvtime = t; + } + + if (!(sendtime&histime&histime1&recvtime)) { + fprintf(stderr, "wrong timestamps\n"); + return -1; + } + + diff = recvtime - sendtime; + /* + * diff can be less than 0 aroud midnight + */ + if (diff < 0) + continue; + rtt = (rtt * 3 + diff)/4; + rtt_sigma = (rtt_sigma *3 + abs(diff-rtt))/4; + msgcount++; + + if (interactive) { + printf("."); + fflush(stdout); + } + + delta1 = histime - sendtime; + /* + * Handles wrap-around to avoid that around + * midnight small time differences appear + * enormous. However, the two machine's clocks + * must be within 12 hours from each other. + */ + if (delta1 < BIASN) + delta1 += MODULO; + else if (delta1 > BIASP) + delta1 -= MODULO; + + delta2 = recvtime - histime1; + if (delta2 < BIASN) + delta2 += MODULO; + else if (delta2 > BIASP) + delta2 -= MODULO; + + if (delta1 < min1) + min1 = delta1; + if (delta2 < min2) + min2 = delta2; + if (delta1 + delta2 < min_rtt) { + min_rtt = delta1 + delta2; + measure_delta1 = (delta1 - delta2)/2 + PROCESSING_TIME; + } + if (diff < RANGE) { + min1 = delta1; + min2 = delta2; + goto good_exit; + } + } + } + } + +good_exit: + measure_delta = (min1 - min2)/2 + PROCESSING_TIME; + return GOOD; +} + + +/* + * Clockdiff computes the difference between the time of the machine on + * which it is called and the time of the machines given as argument. + * The time differences measured by clockdiff are obtained using a sequence + * of ICMP TSTAMP messages which are returned to the sender by the IP module + * in the remote machine. + * In order to compare clocks of machines in different time zones, the time + * is transmitted (as a 32-bit value) in milliseconds since midnight UT. + * If a hosts uses a different time format, it should set the high order + * bit of the 32-bit quantity it transmits. + * However, VMS apparently transmits the time in milliseconds since midnight + * local time (rather than GMT) without setting the high order bit. + * Furthermore, it does not understand daylight-saving time. This makes + * clockdiff behaving inconsistently with hosts running VMS. + * + * In order to reduce the sensitivity to the variance of message transmission + * time, clockdiff sends a sequence of messages. Yet, measures between + * two `distant' hosts can be affected by a small error. The error can, however, + * be reduced by increasing the number of messages sent in each measurement. + */ + +void +usage() { + fprintf(stderr, "Usage: clockdiff [-o] \n"); + exit(1); +} + +void drop_rights(void) { +#ifdef CAPABILITIES + cap_t caps = cap_init(); + if (cap_set_proc(caps)) { + perror("clockdiff: cap_set_proc"); + exit(-1); + } + cap_free(caps); +#endif + if (setuid(getuid())) { + perror("clockdiff: setuid"); + exit(-1); + } +} + +int +main(int argc, char *argv[]) +{ + int measure_status; + struct addrinfo hints = { .ai_family = AF_INET, .ai_socktype = SOCK_RAW, .ai_flags = AI_CANONNAME }; + struct addrinfo *result; + int status; + char hostname[MAX_HOSTNAMELEN]; + int s_errno = 0; + int n_errno = 0; + + if (argc < 2) { + drop_rights(); + usage(); + } + + sock_raw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); + s_errno = errno; + + errno = 0; + if (nice(-16) == -1) + n_errno = errno; + drop_rights(); + + if (argc == 3) { + if (strcmp(argv[1], "-o") == 0) { + ip_opt_len = 4 + 4*8; + argv++; + } else if (strcmp(argv[1], "-o1") == 0) { + ip_opt_len = 4 + 3*8; + argv++; + } else + usage(); + } else if (argc != 2) + usage(); + + if (sock_raw < 0) { + errno = s_errno; + perror("clockdiff: socket"); + exit(1); + } + + if (n_errno < 0) { + errno = n_errno; + perror("clockdiff: nice"); + exit(1); + } + + if (isatty(fileno(stdin)) && isatty(fileno(stdout))) + interactive = 1; + + id = getpid(); + + (void)gethostname(hostname,sizeof(hostname)); + status = getaddrinfo(hostname, NULL, &hints, &result); + if (status) { + fprintf(stderr, "clockdiff: %s: %s\n", hostname, gai_strerror(status)); + exit(2); + } + myname = strdup(result->ai_canonname); + freeaddrinfo(result); + + status = getaddrinfo(argv[1], NULL, &hints, &result); + if (status) { + fprintf(stderr, "clockdiff: %s: %s\n", argv[1], gai_strerror(status)); + exit(1); + } + hisname = strdup(result->ai_canonname); + + memcpy(&server, result->ai_addr, sizeof server); + freeaddrinfo(result); + + if (connect(sock_raw, (struct sockaddr*)&server, sizeof(server)) == -1) { + perror("connect"); + exit(1); + } + if (ip_opt_len) { + struct sockaddr_in myaddr; + socklen_t addrlen = sizeof(myaddr); + unsigned char rspace[ip_opt_len]; + + memset(rspace, 0, sizeof(rspace)); + rspace[0] = IPOPT_TIMESTAMP; + rspace[1] = ip_opt_len; + rspace[2] = 5; + rspace[3] = IPOPT_TS_PRESPEC; + if (getsockname(sock_raw, (struct sockaddr*)&myaddr, &addrlen) == -1) { + perror("getsockname"); + exit(1); + } + ((__u32*)(rspace+4))[0*2] = myaddr.sin_addr.s_addr; + ((__u32*)(rspace+4))[1*2] = server.sin_addr.s_addr; + ((__u32*)(rspace+4))[2*2] = myaddr.sin_addr.s_addr; + if (ip_opt_len == 4+4*8) { + ((__u32*)(rspace+4))[2*2] = server.sin_addr.s_addr; + ((__u32*)(rspace+4))[3*2] = myaddr.sin_addr.s_addr; + } + + if (setsockopt(sock_raw, IPPROTO_IP, IP_OPTIONS, rspace, ip_opt_len) < 0) { + perror("ping: IP_OPTIONS (fallback to icmp tstamps)"); + ip_opt_len = 0; + } + } + + if ((measure_status = (ip_opt_len ? measure_opt : measure)(&server)) < 0) { + if (errno) + perror("measure"); + else + fprintf(stderr, "measure: unknown failure\n"); + exit(1); + } + + switch (measure_status) { + case HOSTDOWN: + fprintf(stderr, "%s is down\n", hisname); + exit(1); + case NONSTDTIME: + fprintf(stderr, "%s time transmitted in a non-standard format\n", hisname); + exit(1); + case UNREACHABLE: + fprintf(stderr, "%s is unreachable\n", hisname); + exit(1); + default: + break; + } + + + { + time_t now = time(NULL); + + if (interactive) + printf("\nhost=%s rtt=%ld(%ld)ms/%ldms delta=%dms/%dms %s", hisname, + rtt, rtt_sigma, min_rtt, + measure_delta, measure_delta1, + ctime(&now)); + else + printf("%ld %d %d\n", now, measure_delta, measure_delta1); + } + exit(0); +} diff --git a/doc/.gitignore b/doc/.gitignore new file mode 100644 index 0000000..085639f --- /dev/null +++ b/doc/.gitignore @@ -0,0 +1,2 @@ +*.html +*.8 diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 0000000..9fc7c83 --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,26 @@ +TARGETS_MAN = arping.8 clockdiff.8 ninfod.8 pg3.8 ping.8 rarpd.8 rdisc.8 tftpd.8 tracepath.8 traceroute6.8 +TARGETS_HTML = arping.html clockdiff.html ninfod.html pg3.html ping.html rarpd.html rdisc.html tftpd.html tracepath.html traceroute6.html + +IPUTILS_VERSION = s$(shell sed 's/[^0-9]*//g' ../SNAPSHOT.h) + +XSLTPROC = /usr/bin/xsltproc +XSLTPROC_FLAGS = --nonet --xinclude --stringparam man.output-quietly 1 --stringparam funcsynopsis.style ansi --stringparam man.authors.section.enabled 0 --stringparam iputils.version $(IPUTILS_VERSION) + +XSL_MAN = custom-man.xsl +XSL_HTML = custom-html.xsl + +%.html: %.xml $(XSL_HTML) + @$(XSLTPROC) -o $@ $(XSLTPROC_FLAGS) $(XSL_HTML) $< +%.8: %.xml $(XSL_MAN) + @$(XSLTPROC) -o $@ $(XSLTPROC_FLAGS) $(XSL_MAN) $< + +all: $(TARGETS_HTML) $(TARGETS_MAN) + +man: $(TARGETS_MAN) +html: $(TARGETS_HTML) + + +clean: + @rm -f *.html + @rm -f *.8 + diff --git a/doc/arping.xml b/doc/arping.xml new file mode 100644 index 0000000..fff9d0f --- /dev/null +++ b/doc/arping.xml @@ -0,0 +1,159 @@ + + + + + + ARPING + 8 + iputils + + + arping + send ARP REQUEST to a neighbour host + + + + + arping + -AbDfhqUV + -c count + -w deadline + -s source + -I interface + destination + + + + + + + DESCRIPTION + Ping destination on device interface by ARP packets, +using source address source. + + +OPTIONS + + + + + The same as , but ARP REPLY packets used instead +of ARP REQUEST. + + + + + + Send only MAC level broadcasts. Normally arping starts +from sending broadcast, and switch to unicast after reply received. + + + + count + + Stop after sending count ARP REQUEST +packets. With deadline option, instead wait for +count ARP REPLY packets, or until the timeout expires. + + + + + Duplicate address detection mode (DAD). See RFC2131, 4.4.1. Returns 0, if DAD succeeded i.e. no replies are received + + + + + Finish after the first reply confirming that target is alive. + + + + interface + Name of network device where to send ARP REQUEST packets. + + + + + Print help page and exit. + + + + + Quiet output. Nothing is displayed. + + + + source + IP source address to use in ARP packets. If this option is absent, source address is: + + + • In DAD mode (with option ) set to 0.0.0.0. + + + + • In Unsolicited ARP mode (with options or ) +set to destination. + + + + • Otherwise, it is calculated from routing tables. + + + + + + + + Unsolicited ARP mode to update neighbours' ARP caches. +No replies are expected. + + + + + Print version of the program and exit. + + + + deadline + Specify a timeout, in seconds, before +arping +exits regardless of how many +packets have been sent or received. In this case +arping +does not stop after +count +packet are sent, it waits either for +deadline +expire or until +count +probes are answered. + + + + + + + SEE ALSO + ping8, +clockdiff8, +tracepath8. + + + + AUTHOR + arping was written by Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>. + + + + SECURITY + arping requires CAP_NET_RAW capability +to be executed. It is not recommended to be used as set-uid root, +because it allows user to modify ARP caches of neighbour hosts. + + + + AVAILABILITY + arping is part of iputils package. + + + diff --git a/doc/clockdiff.xml b/doc/clockdiff.xml new file mode 100644 index 0000000..211cebf --- /dev/null +++ b/doc/clockdiff.xml @@ -0,0 +1,108 @@ + + + + + + CLOCKDIFF + 8 + iputils + + + clockdiff + measure clock difference between hosts + + + + + clockdiff + -o + -o1 + destination + + + + + + + DESCRIPTION + clockdiff Measures clock difference between us and +destination with 1 msec resolution using ICMP TIMESTAMP [2] packets or, optionally, IP TIMESTAMP option [3] option added to ICMP ECHO. +[1] + + + + OPTIONS + + + + +Use IP TIMESTAMP with ICMP ECHO instead of ICMP TIMESTAMP +messages. It is useful with some destinations, which do not support +ICMP TIMESTAMP (f.e. Solaris <2.4). + + + + + +Slightly different form of , namely it uses three-term +IP TIMESTAMP with prespecified hop addresses instead of four term one. +What flavor works better depends on target host. Particularly, + is better for Linux. + + + + + + + WARNINGS + + + • Some nodes (Cisco) use non-standard timestamps, which is allowed by RFC, but makes timestamps mostly useless. + + + + • Some nodes generate messed timestamps (Solaris>2.4), when run xntpd. Seems, its IP stack uses a corrupted clock source, which is synchronized to time-of-day clock periodically and jumps +randomly making timestamps mostly useless. Good news is that you can use NTP in this case, which is even better. + + + + clockdiff shows difference in time modulo 24 days. + + + + + + + SEE ALSO +ping8, +arping8, +tracepath8. + + + + REFERENCES + [1] ICMP ECHO, RFC0792, page 14. + [2] ICMP TIMESTAMP, RFC0792, page 16. + [3] IP TIMESTAMP option, RFC0791, 3.1, page 16. + + + + AUTHOR + clockdiff was compiled by +Alexey Kuznetsov +<kuznet@ms2.inr.ac.ru>. It was based on code borrowed +from BSD timed daemon. + + + + SECURITY + clockdiff requires CAP_NET_RAW capability +to be executed. It is safe to be used as set-uid root. + + + + AVAILABILITY + clockdiff is part of iputils package. + + diff --git a/doc/custom-html.xsl b/doc/custom-html.xsl new file mode 100644 index 0000000..c391535 --- /dev/null +++ b/doc/custom-html.xsl @@ -0,0 +1,279 @@ + + + + + + + + + + + + + .html# + + + + + + + + + + http://man7.org/linux/man-pages/man + + / + + . + + .html + + + + + + + + + http://linux.die.net/man/ + + / + + + + + + + + + + https://www.mankier.com/ + + / + + + + + + + + + + https://www.archlinux.org/ + + / + + . + + .html + + + + + + + + + https://www.freebsd.org/cgi/man.cgi? + + ( + + ) + + + + + + + + + http://dbus.freedesktop.org/doc/ + + . + + .html + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ + + + + + + + + index.html + + Index + · + + + iputils.directives.html + + Directives + + + + iputils + + +
+
+ + + " + + " + + + + +
diff --git a/doc/custom-man.xsl b/doc/custom-man.xsl new file mode 100644 index 0000000..058f495 --- /dev/null +++ b/doc/custom-man.xsl @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + .TH " + + + + + + " " + + " "" "iputils + + " " + + " + + + + + + + + + " + + " + + + diff --git a/doc/ninfod.xml b/doc/ninfod.xml new file mode 100644 index 0000000..3f207b3 --- /dev/null +++ b/doc/ninfod.xml @@ -0,0 +1,113 @@ + + + + + + NINFOD + 8 + iputils + + + ninfod + Respond to IPv6 Node Information Queries + + + + + ninfod + -dhv + -p pidfile + -u user + + + + + + + DESCRIPTION + Responds to IPv6 Node Information Queries (RFC4620) from clients. +Queries can be sent by various implementations of ping6 command. + + + + OPTIONS + + + + +Debug mode. Do not go background. + + + + + +Show help. + + + + + +Verbose mode. + + + + user + +Run as another user. +user can either be username or user ID. + + + + pidfile + +File for process-id storage. +user is required to be able to create the file. + + + + + + + SEE ALSO + ping8. + + + + AUTHOR + ninfod was written by USAGI/WIDE Project. + + +COPYING + +Copyright (C) 2012 YOSHIFUJI Hideaki. +Copyright (C) 2002 USAGI/WIDE Project. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. Neither the name of the project nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS “AS IS” AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + + + + diff --git a/doc/pg3.xml b/doc/pg3.xml new file mode 100644 index 0000000..487a169 --- /dev/null +++ b/doc/pg3.xml @@ -0,0 +1,140 @@ + + + + + + PG3 + 8 + iputils + + + pg3 + ipg + pgset + send stream of UDP packets + + + + + source + ipg + + + + pg + + + + pgset COMMAND + + + + + + + DESCRIPTION + ipg is not a program, it is script which should be sourced +to bash. When sourced it loads module pg3 and +exports a few of functions accessible from parent shell. These macros +are pg to start packet injection and to get the results of run; +and pgset to setup packet generator. + + pgset can send the following commands to module pg3: + + + + COMMAND + + + odev DEVICE + +Name of Ethernet device to test. See +warning below. + + + + pkt_size BYTES + +Size of packet to generate. The size includes all the headers: UDP, IP, +MAC, but does not account for overhead internal to medium, i.e. FCS +and various paddings. + + + + frags NUMBER + +Each packet will contain NUMBER of fragments. +Maximal amount for linux-2.4 is 6. Far not all the devices support +fragmented buffers. + + + + count NUMBER + +Send stream of NUMBER of packets and stop after this. + + + + ipg TIME + +Introduce artificial delay between packets of TIME +microseconds. + + + + dst IP_ADDRESS + +Select IP destination where the stream is sent to. +Beware, never set this address at random. pg3 is not a toy, +it creates really tough stream. Default value is 0.0.0.0. + + + + dst MAC_ADDRESS + +Select MAC destination where the stream is sent to. +Default value is 00:00:00:00:00:00 in hope that this will not be received +by any node on LAN. + + + + stop + +Abort packet injection. + + + + + + + WARNING + When output device is set to some random device different +of hardware Ethernet device, pg3 will crash kernel. + + Do not use it on VLAN, ethertap, VTUN and other devices, +which emulate Ethernet not being real Ethernet in fact. + + + + AUTHOR + pg3 was written by Robert Olsson <robert.olsson@its.uu.se>. + + + + SECURITY + This can be used only by superuser. + + This tool creates floods of packets which is unlikely to be handled +even by high-end machines. For example, it saturates gigabit link with +60 byte packets when used with Intel's e1000. In face of such stream +switches, routers and end hosts may deadlock, crash, explode. +Use only in test lab environment. + + + + AVAILABILITY + pg3 is part of iputils package. + + + diff --git a/doc/ping.xml b/doc/ping.xml new file mode 100644 index 0000000..bdf07b3 --- /dev/null +++ b/doc/ping.xml @@ -0,0 +1,662 @@ + + + + + + PING + 8 + iputils + + + ping + send ICMP ECHO_REQUEST to network hosts + + + + + ping + -aAbBdDfhLnOqrRUvV46 + -c count + -F flowlabel + -i interval + -I interface + -l preload + -m mark + -M pmtudisc_option + -N nodeinfo_option + -w deadline + -W timeout + -p pattern + -Q tos + -s packetsize + -S sndbuf + -t ttl + -T timestampoption + hop + destination + + + + + + + DESCRIPTION + ping uses the ICMP protocol's mandatory ECHO_REQUEST +datagram to elicit an ICMP ECHO_RESPONSE from a host or gateway. +ECHO_REQUEST datagrams (“pings”) have an IP and ICMP +header, followed by a struct timeval and then an arbitrary +number of “pad” bytes used to fill out the packet. + + ping works with both IPv4 and IPv6. Using only one of them +explicitly can be enforced by specifying or . + + ping can also send IPv6 Node Information Queries (RFC4620). +Intermediate hops may not be allowed, because IPv6 source routing was deprecated (RFC5095). + + + + OPTIONS + + + + +Use IPv4 only. + + + + + +Use IPv6 only. + + + + + +Audible ping. + + + + + +Adaptive ping. Interpacket interval adapts to round-trip time, so that +effectively not more than one (or more, if preload is set) unanswered probe +is present in the network. Minimal interval is 200msec for not super-user. +On networks with low rtt this mode is essentially equivalent to flood mode. + + + + + +Allow pinging a broadcast address. + + + + + +Do not allow ping to change source address of probes. +The address is bound to one selected when ping starts. + + + + count + +Stop after sending count ECHO_REQUEST +packets. With deadline option, ping waits for +count ECHO_REPLY packets, until the timeout expires. + + + + + +Set the SO_DEBUG option on the socket being used. +Essentially, this socket option is not used by Linux kernel. + + + + + +Print timestamp (unix time + microseconds as in gettimeofday) before +each line. + + + + + +Flood ping. For every ECHO_REQUEST sent a period “.” is printed, +while for ever ECHO_REPLY received a backspace is printed. +This provides a rapid display of how many packets are being dropped. +If interval is not given, it sets interval to zero and +outputs packets as fast as they come back or one hundred times per second, +whichever is more. +Only the super-user may use this option with zero interval. + + + + flow label + +IPv6 only. +Allocate and set 20 bit flow label (in hex) on echo request packets. +If value is zero, kernel allocates random flow label. + + + + + +Show help. + + + + interval + +Wait interval seconds between sending each packet. +The default is to wait for one second between each packet normally, +or not to wait in flood mode. Only super-user may set interval +to values less than 0.2 seconds. + + + + interface + +interface is either an address, or an interface name. +If interface is an address, it sets source address +to specified interface address. +If interface in an interface name, it sets +source interface to specified interface. +NOTE: For IPv6, when doing ping to a link-local scope +address, link specification (by the '%'-notation in +destination, or by this option) +can be used but it is no longer required. + + + + preload + +If preload is specified, +ping sends that many packets not waiting for reply. +Only the super-user may select preload more than 3. + + + + + +Suppress loopback of multicast packets. This flag only applies if the ping +destination is a multicast address. + + + + mark + +use mark to tag the packets going out. This is useful +for variety of reasons within the kernel such as using policy +routing to select specific outbound processing. + + + + pmtudisc_opt + +Select Path MTU Discovery strategy. +pmtudisc_option may be either do +(prohibit fragmentation, even local one), +want (do PMTU discovery, fragment locally when packet size +is large), or dont (do not set DF flag). + + + + nodeinfo_option + +IPv6 only. +Send ICMPv6 Node Information Queries (RFC4620), instead of Echo Request. +CAP_NET_RAW capability is required. + + + help + +Show help for NI support. + + + + + + name + +Queries for Node Names. + + + + + + ipv6 + +Queries for IPv6 Addresses. There are several IPv6 specific flags. + + + ipv6-global + +Request IPv6 global-scope addresses. + + + + + + ipv6-sitelocal + +Request IPv6 site-local addresses. + + + + + + ipv6-linklocal + +Request IPv6 link-local addresses. + + + + + + ipv6-all + +Request IPv6 addresses on other interfaces. + + + + + + + + + ipv4 + +Queries for IPv4 Addresses. There is one IPv4 specific flag. + + + ipv4-all + +Request IPv4 addresses on other interfaces. + + + + + + + + + subject-ipv6=ipv6addr + +IPv6 subject address. + + + + + + subject-ipv4=ipv4addr + +IPv4 subject address. + + + + + + subject-name=nodename + +Subject name. If it contains more than one dot, +fully-qualified domain name is assumed. + + + + + + subject-fqdn=nodename + +Subject name. Fully-qualified domain name is +always assumed. + + + + + + + + +Numeric output only. +No attempt will be made to lookup symbolic names for host addresses. + + + + + +Report outstanding ICMP ECHO reply before sending next packet. +This is useful together with the timestamp to +log output to a diagnostic file and search for missing answers. + + + + pattern + +You may specify up to 16 “pad” bytes to fill out the packet you send. +This is useful for diagnosing data-dependent problems in a network. +For example, will cause the sent packet +to be filled with all ones. + + + + + +Quiet output. +Nothing is displayed except the summary lines at startup time and +when finished. + + + + tos + +Set Quality of Service -related bits in ICMP datagrams. +tos can be decimal (ping only) or hex number. + +In RFC2474, these fields are interpreted as 8-bit Differentiated +Services (DS), consisting of: bits 0-1 (2 lowest bits) of separate +data, and bits 2-7 (highest 6 bits) of Differentiated Services +Codepoint (DSCP). In RFC2481 and RFC3168, bits 0-1 are used for ECN. + +Historically (RFC1349, obsoleted by RFC2474), these were interpreted +as: bit 0 (lowest bit) for reserved (currently being redefined as +congestion control), 1-4 for Type of Service and bits 5-7 +(highest bits) for Precedence. + + + + + +Bypass the normal routing tables and send directly to a host on an attached +interface. +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 provided the option is also +used. + + + + + +ping only. +Record route. +Includes the RECORD_ROUTE option in the ECHO_REQUEST +packet and displays the route buffer on returned packets. +Note that the IP header is only large enough for nine such routes. +Many hosts ignore or discard this option. + + + + packetsize + +Specifies the number of data bytes to be sent. +The default is 56, which translates into 64 ICMP +data bytes when combined with the 8 bytes of ICMP header data. + + + + sndbuf + +Set socket sndbuf. If not specified, it is selected to buffer +not more than one packet. + + + + ttl + +ping only. +Set the IP Time to Live. + + + + timestamp option + +Set special IP timestamp options. +timestamp option may be either +tsonly (only timestamps), +tsandaddr (timestamps and addresses) or +tsprespec host1 [host2 [host3 [host4]]] +(timestamp prespecified hops). + + + + + +Print full user-to-user latency (the old behaviour). Normally +ping +prints network round trip time, which can be different +f.e. due to DNS failures. + + + + + +Verbose output. + + + + + +Show version and exit. + + + + deadline + +Specify a timeout, in seconds, before +ping +exits regardless of how many +packets have been sent or received. In this case +ping +does not stop after +count +packet are sent, it waits either for +deadline +expire or until +count +probes are answered or for some error notification from network. + + + + timeout + +Time to wait for a response, in seconds. The option affects only timeout +in absence of any responses, otherwise ping waits for two RTTs. + + + +When using ping for fault isolation, it should first be run +on the local host, to verify that the local network interface is up +and running. Then, hosts and gateways further and further away should be +“pinged”. Round-trip times and packet loss statistics are computed. +If duplicate packets are received, they are not included in the packet +loss calculation, although the round trip time of these packets is used +in calculating the minimum/average/maximum/mdev round-trip time numbers. + + +Median deviation (mdev), essentially an average of how far each ping RTT is from the mean RTT. The higher mdev is, the more variable the RTT is (over time). + +With a high RTT variability, you will have speed issues with bulk transfers (they will take longer than is strictly speaking necessary, as the variability will eventually cause the sender to wait for ACKs) and you will have middling to poor VoIP quality. + + +When the specified number of packets have been sent (and received) or +if the program is terminated with a +SIGINT, a brief summary is displayed. Shorter current statistics +can be obtained without termination of process with signal +SIGQUIT. + +If ping does not receive any reply packets at all it will +exit with code 1. If a packet +count +and +deadline +are both specified, and fewer than +count +packets are received by the time the +deadline +has arrived, it will also exit with code 1. +On other error it exits with code 2. Otherwise it exits with code 0. This +makes it possible to use the exit code to see if a host is alive or +not. + +This program is intended for use in network testing, measurement and +management. +Because of the load it can impose on the network, it is unwise to use +ping during normal operations or from automated scripts. + + +ICMP PACKET DETAILS +An IP header without options is 20 bytes. +An ICMP ECHO_REQUEST packet contains an additional 8 bytes worth +of ICMP header followed by an arbitrary amount of data. +When a packetsize is given, this indicated the size of this +extra piece of data (the default is 56). Thus the amount of data received +inside of an IP packet of type ICMP ECHO_REPLY will always be 8 bytes +more than the requested data space (the ICMP header). + +If the data space is at least of size of struct timeval +ping uses the beginning bytes of this space to include +a timestamp which it uses in the computation of round trip times. +If the data space is shorter, no round trip times are given. + + +DUPLICATE AND DAMAGED PACKETS +ping will report duplicate and damaged packets. +Duplicate packets should never occur, and seem to be caused by +inappropriate link-level retransmissions. +Duplicates may occur in many situations and are rarely (if ever) a +good sign, although the presence of low levels of duplicates may not +always be cause for alarm. + +Damaged packets are obviously serious cause for alarm and often +indicate broken hardware somewhere in the +ping packet's path (in the network or in the hosts). + + + + TRYING DIFFERENT DATA PATTERNS +The (inter)network layer should never treat packets differently depending +on the data contained in the data portion. +Unfortunately, data-dependent problems have been known to sneak into +networks and remain undetected for long periods of time. +In many cases the particular pattern that will have problems is something +that doesn't have sufficient “transitions”, such as all ones or all +zeros, or a pattern right at the edge, such as almost all zeros. +It isn't necessarily enough to specify a data pattern of all zeros (for +example) on the command line because the pattern that is of interest is +at the data link level, and the relationship between what you type and +what the controllers transmit can be complicated. + +This means that if you have a data-dependent problem you will probably +have to do a lot of testing to find it. +If you are lucky, you may manage to find a file that either can't be sent +across your network or that takes much longer to transfer than other +similar length files. +You can then examine this file for repeated patterns that you can test +using the option of ping. + + +TTL DETAILS +The TTL value of an IP packet represents the maximum number of IP routers +that the packet can go through before being thrown away. +In current practice you can expect each router in the Internet to decrement +the TTL field by exactly one. + +The TCP/IP specification states that the TTL field for TCP +packets should be set to 60, but many systems use smaller values +(4.3 BSD uses 30, 4.2 used 15). + +The maximum possible value of this field is 255, and most Unix systems set +the TTL field of ICMP ECHO_REQUEST packets to 255. +This is why you will find you can “ping” some hosts, but not reach them +with +telnet1 +or +ftp1. + +In normal operation ping prints the TTL value from the packet it receives. +When a remote system receives a ping packet, it can do one of three things +with the TTL field in its response: + + + +• Not change it; this is what Berkeley Unix systems did before the +4.3BSD Tahoe release. In this case the TTL value in the received packet +will be 255 minus the number of routers in the round-trip path. + + + + +• Set it to 255; this is what current Berkeley Unix systems do. +In this case the TTL value in the received packet will be 255 minus the +number of routers in the path from +the remote system to the pinging host. + + + + +• Set it to some other value. Some machines use the same value for +ICMP packets that they use for TCP packets, for example either 30 or 60. +Others may use completely wild values. + + + + + +BUGS + + + +• Many Hosts and Gateways ignore the RECORD_ROUTE option. + + + + +• The maximum IP header length is too small for options like +RECORD_ROUTE to be completely useful. +There's not much that can be done about this, however. + + + + +• Flood pinging is not recommended in general, and flood pinging the +broadcast address should only be done under very controlled conditions. + + + + + + + SEE ALSO + ip8, +ss8. + + + + HISTORY + The ping command appeared in 4.3BSD. + + The version described here is its descendant specific to Linux. + + As of version s20150815, the ping6 binary doesn't exist anymore. +It has been merged into ping. Creating a symlink named +ping6 pointing to ping will result in the same +funcionality as before. + + + + SECURITY +ping requires CAP_NET_RAW capability +to be executed 1) if the program is used for non-echo queries +(See option), or 2) if kernel does not +support non-raw ICMP sockets, or 3) if the user is not allowed +to create an ICMP echo socket. The program may be used as +set-uid root. + + + + AVAILABILITY + ping is part of iputils package. + + diff --git a/doc/rarpd.xml b/doc/rarpd.xml new file mode 100644 index 0000000..8d3ece8 --- /dev/null +++ b/doc/rarpd.xml @@ -0,0 +1,124 @@ + + + + + + RARPD + 8 + iputils + + + rarpd + answer RARP REQUESTs + + + + + arping + -aAvde + -b bootdir + interface + + + + + + + DESCRIPTION + Listens RARP requests from clients. Provided MAC address of client +is found in /etc/ethers database and +obtained host name is resolvable to an IP address appropriate +for attached network, rarpd answers to client with RARPD +reply carrying an IP address. + + To allow multiple boot servers on the network rarpd +optionally checks for presence Sun-like bootable image in TFTP directory. +It should have form Hexadecimal_IP.ARCH, f.e. to load +sparc 193.233.7.98 C1E90762.SUN4M is linked to +an image appropriate for SUM4M in directory /etc/tftpboot. + + + + WARNING + This facility is deeply obsoleted by +BOOTP +and later +DHCP protocols. +However, some clients really still need this to boot. + + + + OPTIONS + + + + +Listen on all the interfaces. Currently it is an internal +option, its function is overridden with interface +argument. It should not be used. + + + + + +Listen not only RARP but also ARP messages, some rare clients +use ARP by some unknown reason. + + + + + +Be verbose. + + + + + +Debug mode. Do not go to background. + + + + + +Do not check for presence of a boot image, reply if MAC address +resolves to a valid IP address using /etc/ethers +database and DNS. + + + + bootdir + +TFTP boot directory. Default is /etc/tftpboot + + + + + + + SEE ALSO + arping8, +tftpd8. + + + + AUTHOR + rarpd was written by Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>. + + + + SECURITY + rarpd requires CAP_NET_RAW capability +to listen and send RARP and ARP packets. It also needs CAP_NET_ADMIN +to give to kernel hint for ARP resolution; this is not strictly required, +but some (most of, to be more exact) clients are so badly broken that +are not able to answer ARP before they are finally booted. This is +not wonderful taking into account that clients using RARPD in 2002 +are all unsupported relic creatures of 90's and even earlier. + + + + AVAILABILITY + rarpd is part of iputils package. + + diff --git a/doc/rdisc.xml b/doc/rdisc.xml new file mode 100644 index 0000000..73a9dee --- /dev/null +++ b/doc/rdisc.xml @@ -0,0 +1,182 @@ + + + + + + RDISC + 8 + iputils + + + rdisc + network router discovery daemon + + + + + rdisc + -abdfrstvV + -p preference + -T max_interval + send_address + receive_address + + + + + + DESCRIPTION + rdisc implements client side of the ICMP router discover protocol. +rdisc is invoked at boot time to populate the network +routing tables with default routes. + + rdisc listens on the ALL_HOSTS (224.0.0.1) multicast address +(or receive_address provided it is given) +for ROUTER_ADVERTISE messages from routers. The received +messages are handled by first ignoring those listed router addresses +with which the host does not share a network. Among the remaining addresses +the ones with the highest preference are selected as default routers +and a default route is entered in the kernel routing table +for each one of them. + + Optionally, rdisc can avoid waiting for routers to announce +themselves by sending out a few ROUTER_SOLICITATION messages +to the ALL_ROUTERS (224.0.0.2) multicast address +(or send_address provided it is given) +when it is started. + + A timer is associated with each router address and the address will +no longer be considered for inclusion in the the routing tables if the +timer expires before a new +advertise message is received from the router. +The address will also be excluded from consideration if the host receives an +advertise +message with the preference being maximally negative. + + Server side of router discovery protocol is supported by Cisco IOS +and by any more or less complete UNIX routing daemon, f.e gated. +Or, rdisc can act as responder, if compiled with -DRDISC_SERVER. + + + + OPTIONS + + + + +Accept all routers independently of the preference they have in their +advertise messages. +Normally rdisc only accepts (and enters in the kernel routing +tables) the router or routers with the highest preference. + + + + + +Opposite to , i.e. install only router with the best +preference value. It is default behaviour. + + + + + +Send debugging messages to syslog. + + + + + +Run rdisc forever even if no routers are found. +Normally rdisc gives up if it has not received any +advertise message after after soliciting three times, +in which case it exits with a non-zero exit code. +If is not specified in the first form then + must be specified. + + + + + +Responder mode, available only if compiled with -DRDISC_SERVER. + + + + + +Send three solicitation messages initially to quickly discover +the routers when the system is booted. +When is specified rdisc +exits with a non-zero exit code if it can not find any routers. +This can be overridden with the option. + + + + preference + +Set preference in advertisement. +Available only with -r option. + + + + max_interval + +Set maximum advertisement interval in seconds. Default is 600 secs. +Available only with -r option. + + + + + +Test mode. Do not go to background. + + + + + +Be verbose i.e. send lots of debugging messages to syslog. + + + + + +Print version and exit. + + + + + + + HISTORY + This program was developed by Sun Microsystems (see copyright +notice in source file). It was ported to Linux by +Alexey Kuznetsov<kuznet@ms2.inr.ac.ru>. + + + + SEE ALSO + icmp7, +inet7, +ping8. + + + + REFERENCES + Deering, S.E.,ed "ICMP Router Discovery Messages", +RFC1256, Network Information Center, SRI International, +Menlo Park, Calif., September 1991. + + + + SECURITY + rdisc requires CAP_NET_RAW to listen +and send ICMP messages and capability CAP_NET_ADMIN +to update routing tables. + + + + AVAILABILITY + rdisc is part of iputils package. + + + diff --git a/doc/tftpd.xml b/doc/tftpd.xml new file mode 100644 index 0000000..9a362e7 --- /dev/null +++ b/doc/tftpd.xml @@ -0,0 +1,104 @@ + + + + + + TFTPD + 8 + iputils + + + tftpd + Trivial File Transfer Protocol server + + + + + tftpd + directory + + + + + + + DESCRIPTION + tftpd is a server which supports the DARPA +Trivial File Transfer Protocol (RFC1350). +The TFTP server is started by +inetd8. + +directory is required argument; if it is not given +tftpd aborts. This path is prepended to any file name requested +via TFTP protocol, effectively chrooting tftpd to this directory. +File names are validated not to escape out of this directory, however +administrator may configure such escape using symbolic links. + +It is in difference of variants of tftpd usually distributed +with unix-like systems, which take a list of directories and match +file names to start from one of given prefixes or to some random +default, when no arguments were given. There are two reasons not to +behave in this way: first, it is inconvenient, clients are not expected +to know something about layout of filesystem on server host. +And second, TFTP protocol is not a tool for browsing of server's filesystem, +it is just an agent allowing to boot dumb clients. + +In the case when tftpd is used together with +rarpd8, +tftp directories in these services should coincide and it is expected +that each client booted via TFTP has boot image corresponding +its IP address with an architecture suffix following Sun Microsystems +conventions. See +rarpd8 +for more details. + + + + SECURITY + TFTP protocol does not provide any authentication. +Due to this capital flaw tftpd is not able to restrict +access to files and will allow only publically readable +files to be accessed. Files may be written only if they already +exist and are publically writable. + +Impact is evident, directory exported via TFTP must not +contain sensitive information of any kind, everyone is allowed +to read it as soon as a client is allowed. Boot images do not contain +such information as rule, however you should think twice before +publishing f.e. Cisco IOS config files via TFTP, they contain +unencrypted passwords and may contain some information +about the network, which you were not going to make public. + +The tftpd server should be executed by inetd +with dropped root privileges, namely with a user ID giving minimal +access to files published in tftp directory. If it is executed +as superuser occasionally, tftpd drops its UID and GID +to 65534, which is most likely not the thing which you expect. +However, this is not very essential; remember, only files accessible +for everyone can be read or written via TFTP. + + + + SEE ALSO + rarpd8, +tftp1, +inetd8. + + + + HISTORY + The tftpd command appeared in 4.2BSD. The source in iputils +is cleaned up both syntactically (ANSIized) and semantically (UDP socket IO). + +It is distributed with iputils mostly as good demo of an interesting feature +(MSG_CONFIRM) allowing to boot long images by dumb clients +not answering ARP requests until they are finally booted. +However, this is full functional and can be used in production. + + + + AVAILABILITY + tftpd is part of iputils package. + + diff --git a/doc/tracepath.xml b/doc/tracepath.xml new file mode 100644 index 0000000..e44e56a --- /dev/null +++ b/doc/tracepath.xml @@ -0,0 +1,165 @@ + + + + + + TRACEPATH + 8 + iputils + + + tracepath + tracepath6 + traces path to a network host discovering MTU along this path + + + + + tracepath + -4 + -6 + -n + -b + -l pktlen + -m max_hops + -p port + destination + + + + + + + DESCRIPTION + It traces path to destination discovering MTU along this path. +It uses UDP port port or some random port. +It is similar to traceroute, only does not require superuser +privileges and has no fancy options. + + tracepath6 is good replacement for traceroute6 +and classic example of application of Linux error queues. +The situation with IPv4 is worse, because commercial +IP routers do not return enough information in ICMP error messages. +Probably, it will change, when they will be updated. +For now it uses Van Jacobson's trick, sweeping a range +of UDP ports to maintain trace history. + + + + OPTIONS + + + + +Use IPv4 only.. + + + + + +Use IPv6 only.. + + + + + +Print primarily IP addresses numerically. + + + + + +Print both of host names and IP addresses. + + + + + +Sets the initial packet length to pktlen instead of +65535 for tracepath or 128000 for tracepath6. + + + + + +Set maximum hops (or maximum TTLs) to max_hops +instead of 30. + + + + + +Sets the initial destination port to use. + + + + + + + OUTPUT + +root@mops:~ # tracepath6 3ffe:2400:0:109::2 + 1?: [LOCALHOST] pmtu 1500 + 1: dust.inr.ac.ru 0.411ms + 2: dust.inr.ac.ru asymm 1 0.390ms pmtu 1480 + 2: 3ffe:2400:0:109::2 463.514ms reached + Resume: pmtu 1480 hops 2 back 2 + + +The first column shows TTL of the probe, followed by colon. +Usually value of TTL is obtained from reply from network, +but sometimes reply does not contain necessary information and +we have to guess it. In this case the number is followed by ?. + +The second column shows the network hop, which replied to the probe. +It is either address of router or word [LOCALHOST], if +the probe was not sent to the network. + +The rest of line shows miscellaneous information about path to +the correspinding network hop. As rule it contains value of RTT. +Additionally, it can show Path MTU, when it changes. +If the path is asymmetric +or the probe finishes before it reach prescribed hop, difference +between number of hops in forward and backward direction is shown +following keyword async. This information is not reliable. +F.e. the third line shows asymmetry of 1, it is because the first probe +with TTL of 2 was rejected at the first hop due to Path MTU Discovery. + +The last line summarizes information about all the path to the destination, +it shows detected Path MTU, amount of hops to the destination and our +guess about amount of hops from the destination to us, which can be +different when the path is asymmetric. + + + + SEE ALSO + traceroute8, +traceroute68, +ping8. + + + + AUTHOR + tracepath was written by +Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>. + + + + SECURITY + No security issues. + + This lapidary deserves to be elaborated. +tracepath is not a privileged program, unlike +traceroute, ping and other beasts of this kind. +tracepath may be executed by everyone who has some access +to network, enough to send UDP datagrams to investigated destination +using given port. + + + + AVAILABILITY + tracepath is part of iputils package. + + + diff --git a/doc/traceroute6.xml b/doc/traceroute6.xml new file mode 100644 index 0000000..58cb412 --- /dev/null +++ b/doc/traceroute6.xml @@ -0,0 +1,69 @@ + + + + + + TRACEROUTE6 + 8 +iputils + + + traceroute6 + traces path to a network host + + + + + traceroute6 + -dnrvV + -i interface + -m max_ttl + -p port + -q max_probes + -s source + -w waittime + destination + size + + + + + + + DESCRIPTION + Description can be found in +traceroute8, +all the references to IP replaced to IPv6. It is needless to copy +the description from there. + + + + SEE ALSO + traceroute8, +tracepath8, +ping8. + + + + HISTORY + This program has long history. Author of traceroute +is Van Jacobson and it first appeared in 1988. This clone is +based on a port of traceroute to IPv6 published +in NRL IPv6 distribution in 1996. In turn, it was ported +to Linux by Pedro Roque. After this it was kept in sync by Alexey Kuznetsov +<kuznet@ms2.inr.ac.ru>. And eventually entered +iputils package. + + + + SECURITY + tracepath6 requires CAP_NET_RAW capability +to be executed. It is safe to be used as set-uid root. + + + + AVAILABILITY + traceroute6 is part of iputils package. + + diff --git a/ifenslave.8 b/ifenslave.8 new file mode 100644 index 0000000..8999892 --- /dev/null +++ b/ifenslave.8 @@ -0,0 +1,68 @@ +.Dd 2004-04-09 +.Dt IFENSLAVE 8 +.\" Manual page created by Guus Sliepen +.Sh NAME +.Nm ifenslave +.Nd Attach and detach slave network devices to a bonding device. +.Sh SYNOPSIS +.Nm +.Op Fl acdfhuvV +.Op Fl -all-interfaces +.Op Fl -change-active +.Op Fl -detach +.Op Fl -force +.Op Fl -help +.Op Fl -usage +.Op Fl -verbose +.Op Fl -version +.Ar master +.Ar slave +.No ... +.Sh DESCRIPTION +.Nm +is a tool to attach and detach slave network devices to a bonding device. +A bonding device will act like a normal Ethernet network device to the kernel, +but will send out the packets via the slave devices using a simple round-robin scheduler. +This allows for simple load-balancing, +identical to "channel bonding" or "trunking" techniques used in switches. +.Pp +The kernel must have support for bonding devices for +.Nm +to be useful. +.Sh OPTIONS +.Bl -tag -width indent +.It Fl a, -all-interfaces +Show information about all interfaces. +.It Fl c, -change-active +Change active slave. +.It Fl d, -detach +Removes slave interfaces from the bonding device. +.It Fl f, -force +Force actions to be taken if one of the specified interfaces appears not to belong to an Ethernet device. +.It Fl h, -help +Display a help message and exit. +.It Fl u, -usage +Show usage information and exit. +.It Fl v, -verbose +Print warning and debug messages. +.It Fl V, -version +Show version information and exit. +.El +If not options are given, the default action will be to enslave interfaces. +.Sh EXAMPLE +The following example shows how to setup a bonding device and +enslave two real Ethernet devices to it: +.Bd -literal +# modprobe bonding +# ifconfig bond0 192.168.0.1 netmask 255.255.0.0 +# ifenslave bond0 eth0 eth1 +.Ed +.Sh AUTHOR +.Nm +was originally written by +.An Donald Becker Aq becker@cesdis.gsfc.nasa.gov , +and has since been updated by various kernel developers. +.Pp +This manual page was written by +.An Guus Sliepen Aq guus@debian.org +for the Debian GNU/Linux system. diff --git a/ifenslave.c b/ifenslave.c new file mode 100644 index 0000000..ac5debb --- /dev/null +++ b/ifenslave.c @@ -0,0 +1,1105 @@ +/* Mode: C; + * ifenslave.c: Configure network interfaces for parallel routing. + * + * This program controls the Linux implementation of running multiple + * network interfaces in parallel. + * + * Author: Donald Becker + * Copyright 1994-1996 Donald Becker + * + * 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. + * + * The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O + * Center of Excellence in Space Data and Information Sciences + * Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771 + * + * Changes : + * - 2000/10/02 Willy Tarreau : + * - few fixes. Master's MAC address is now correctly taken from + * the first device when not previously set ; + * - detach support : call BOND_RELEASE to detach an enslaved interface. + * - give a mini-howto from command-line help : # ifenslave -h + * + * - 2001/02/16 Chad N. Tindel : + * - Master is now brought down before setting the MAC address. In + * the 2.4 kernel you can't change the MAC address while the device is + * up because you get EBUSY. + * + * - 2001/09/13 Takao Indoh + * - Added the ability to change the active interface on a mode 1 bond + * at runtime. + * + * - 2001/10/23 Chad N. Tindel : + * - No longer set the MAC address of the master. The bond device will + * take care of this itself + * - Try the SIOC*** versions of the bonding ioctls before using the + * old versions + * - 2002/02/18 Erik Habbinga : + * - ifr2.ifr_flags was not initialized in the hwaddr_notset case, + * SIOCGIFFLAGS now called before hwaddr_notset test + * + * - 2002/10/31 Tony Cureington : + * - If the master does not have a hardware address when the first slave + * is enslaved, the master is assigned the hardware address of that + * slave - there is a comment in bonding.c stating "ifenslave takes + * care of this now." This corrects the problem of slaves having + * different hardware addresses in active-backup mode when + * multiple interfaces are specified on a single ifenslave command + * (ifenslave bond0 eth0 eth1). + * + * - 2003/03/18 - Tsippy Mendelson and + * Shmulik Hen + * - Moved setting the slave's mac address and openning it, from + * the application to the driver. This enables support of modes + * that need to use the unique mac address of each slave. + * The driver also takes care of closing the slave and restoring its + * original mac address upon release. + * In addition, block possibility of enslaving before the master is up. + * This prevents putting the system in an undefined state. + * + * - 2003/05/01 - Amir Noam + * - Added ABI version control to restore compatibility between + * new/old ifenslave and new/old bonding. + * - Prevent adding an adapter that is already a slave. + * Fixes the problem of stalling the transmission and leaving + * the slave in a down state. + * + * - 2003/05/01 - Shmulik Hen + * - Prevent enslaving if the bond device is down. + * Fixes the problem of leaving the system in unstable state and + * halting when trying to remove the module. + * - Close socket on all abnormal exists. + * - Add versioning scheme that follows that of the bonding driver. + * current version is 1.0.0 as a base line. + * + * - 2003/05/22 - Jay Vosburgh + * - ifenslave -c was broken; it's now fixed + * - Fixed problem with routes vanishing from master during enslave + * processing. + * + * - 2003/05/27 - Amir Noam + * - Fix backward compatibility issues: + * For drivers not using ABI versions, slave was set down while + * it should be left up before enslaving. + * Also, master was not set down and the default set_mac_address() + * would fail and generate an error message in the system log. + * - For opt_c: slave should not be set to the master's setting + * while it is running. It was already set during enslave. To + * simplify things, it is now handled separately. + * + * - 2003/12/01 - Shmulik Hen + * - Code cleanup and style changes + * set version to 1.1.0 + */ + +#define APP_VERSION "1.1.0" +#define APP_RELDATE "December 1, 2003" +#define APP_NAME "ifenslave" + +static char *version = +APP_NAME ".c:v" APP_VERSION " (" APP_RELDATE ")\n" +"o Donald Becker (becker@cesdis.gsfc.nasa.gov).\n" +"o Detach support added on 2000/10/02 by Willy Tarreau (willy at meta-x.org).\n" +"o 2.4 kernel support added on 2001/02/16 by Chad N. Tindel\n" +" (ctindel at ieee dot org).\n"; + +static const char *usage_msg = +"Usage: ifenslave [-f] [...]\n" +" ifenslave -d [...]\n" +" ifenslave -c \n" +" ifenslave --help\n"; + +static const char *help_msg = +"\n" +" To create a bond device, simply follow these three steps :\n" +" - ensure that the required drivers are properly loaded :\n" +" # modprobe bonding ; modprobe <3c59x|eepro100|pcnet32|tulip|...>\n" +" - assign an IP address to the bond device :\n" +" # ifconfig bond0 netmask broadcast \n" +" - attach all the interfaces you need to the bond device :\n" +" # ifenslave [{-f|--force}] bond0 eth0 [eth1 [eth2]...]\n" +" If bond0 didn't have a MAC address, it will take eth0's. Then, all\n" +" interfaces attached AFTER this assignment will get the same MAC addr.\n" +" (except for ALB/TLB modes)\n" +"\n" +" To set the bond device down and automatically release all the slaves :\n" +" # ifconfig bond0 down\n" +"\n" +" To detach a dead interface without setting the bond device down :\n" +" # ifenslave {-d|--detach} bond0 eth0 [eth1 [eth2]...]\n" +"\n" +" To change active slave :\n" +" # ifenslave {-c|--change-active} bond0 eth0\n" +"\n" +" To show master interface info\n" +" # ifenslave bond0\n" +"\n" +" To show all interfaces info\n" +" # ifenslave {-a|--all-interfaces}\n" +"\n" +" To be more verbose\n" +" # ifenslave {-v|--verbose} ...\n" +"\n" +" # ifenslave {-u|--usage} Show usage\n" +" # ifenslave {-V|--version} Show version\n" +" # ifenslave {-h|--help} This message\n" +"\n"; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef unsigned long long u64; /* hack, so we may include kernel's ethtool.h */ +typedef __uint32_t u32; /* ditto */ +typedef __uint16_t u16; /* ditto */ +typedef __uint8_t u8; /* ditto */ +#include + +struct option longopts[] = { + /* { name has_arg *flag val } */ + {"all-interfaces", 0, 0, 'a'}, /* Show all interfaces. */ + {"change-active", 0, 0, 'c'}, /* Change the active slave. */ + {"detach", 0, 0, 'd'}, /* Detach a slave interface. */ + {"force", 0, 0, 'f'}, /* Force the operation. */ + {"help", 0, 0, 'h'}, /* Give help */ + {"usage", 0, 0, 'u'}, /* Give usage */ + {"verbose", 0, 0, 'v'}, /* Report each action taken. */ + {"version", 0, 0, 'V'}, /* Emit version information. */ + { 0, 0, 0, 0} +}; + +/* Command-line flags. */ +unsigned int +opt_a = 0, /* Show-all-interfaces flag. */ +opt_c = 0, /* Change-active-slave flag. */ +opt_d = 0, /* Detach a slave interface. */ +opt_f = 0, /* Force the operation. */ +opt_h = 0, /* Help */ +opt_u = 0, /* Usage */ +opt_v = 0, /* Verbose flag. */ +opt_V = 0; /* Version */ + +int skfd = -1; /* AF_INET socket for ioctl() calls.*/ +int abi_ver = 0; /* userland - kernel ABI version */ +int hwaddr_set = 0; /* Master's hwaddr is set */ +int saved_errno; + +struct ifreq master_mtu, master_flags, master_hwaddr; +struct ifreq slave_mtu, slave_flags, slave_hwaddr; + +struct dev_ifr { + struct ifreq *req_ifr; + char *req_name; + int req_type; +}; + +struct dev_ifr master_ifra[] = { + {&master_mtu, "SIOCGIFMTU", SIOCGIFMTU}, + {&master_flags, "SIOCGIFFLAGS", SIOCGIFFLAGS}, + {&master_hwaddr, "SIOCGIFHWADDR", SIOCGIFHWADDR}, + {NULL, "", 0} +}; + +struct dev_ifr slave_ifra[] = { + {&slave_mtu, "SIOCGIFMTU", SIOCGIFMTU}, + {&slave_flags, "SIOCGIFFLAGS", SIOCGIFFLAGS}, + {&slave_hwaddr, "SIOCGIFHWADDR", SIOCGIFHWADDR}, + {NULL, "", 0} +}; + +static void if_print(char *ifname); +static int get_drv_info(char *master_ifname); +static int get_if_settings(char *ifname, struct dev_ifr ifra[]); +static int get_slave_flags(char *slave_ifname); +static int set_master_hwaddr(char *master_ifname, struct sockaddr *hwaddr); +static int set_slave_hwaddr(char *slave_ifname, struct sockaddr *hwaddr); +static int set_slave_mtu(char *slave_ifname, int mtu); +static int set_if_flags(char *ifname, short flags); +static int set_if_up(char *ifname, short flags); +static int set_if_down(char *ifname, short flags); +static int clear_if_addr(char *ifname); +static int set_if_addr(char *master_ifname, char *slave_ifname); +static int change_active(char *master_ifname, char *slave_ifname); +static int enslave(char *master_ifname, char *slave_ifname); +static int release(char *master_ifname, char *slave_ifname); +#define v_print(fmt, args...) \ + if (opt_v) \ + fprintf(stderr, fmt, ## args ) + +int main(int argc, char *argv[]) +{ + char **spp, *master_ifname, *slave_ifname; + int c, i, rv; + int res = 0; + int exclusive = 0; + + while ((c = getopt_long(argc, argv, "acdfhuvV", longopts, 0)) != EOF) { + switch (c) { + case 'a': opt_a++; exclusive++; break; + case 'c': opt_c++; exclusive++; break; + case 'd': opt_d++; exclusive++; break; + case 'f': opt_f++; exclusive++; break; + case 'h': opt_h++; exclusive++; break; + case 'u': opt_u++; exclusive++; break; + case 'v': opt_v++; break; + case 'V': opt_V++; exclusive++; break; + + case '?': + fprintf(stderr, "%s", usage_msg); + res = 2; + goto out; + } + } + + /* options check */ + if (exclusive > 1) { + fprintf(stderr, "%s", usage_msg); + res = 2; + goto out; + } + + if (opt_v || opt_V) { + printf("%s", version); + if (opt_V) { + res = 0; + goto out; + } + } + + if (opt_u) { + printf("%s", usage_msg); + res = 0; + goto out; + } + + if (opt_h) { + printf("%s", usage_msg); + printf("%s", help_msg); + res = 0; + goto out; + } + + /* Open a basic socket */ + if ((skfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + perror("socket"); + res = 1; + goto out; + } + + if (opt_a) { + if (optind == argc) { + /* No remaining args */ + /* show all interfaces */ + if_print((char *)NULL); + goto out; + } else { + /* Just show usage */ + fprintf(stderr, "%s", usage_msg); + res = 2; + goto out; + } + } + + /* Copy the interface name */ + spp = argv + optind; + master_ifname = *spp++; + + if (master_ifname == NULL) { + fprintf(stderr, "%s", usage_msg); + res = 2; + goto out; + } + + /* exchange abi version with bonding module */ + res = get_drv_info(master_ifname); + if (res) { + fprintf(stderr, + "Master '%s': Error: handshake with driver failed. " + "Aborting\n", + master_ifname); + goto out; + } + + slave_ifname = *spp++; + + if (slave_ifname == NULL) { + if (opt_d || opt_c) { + fprintf(stderr, "%s", usage_msg); + res = 2; + goto out; + } + + /* A single arg means show the + * configuration for this interface + */ + if_print(master_ifname); + goto out; + } + + res = get_if_settings(master_ifname, master_ifra); + if (res) { + /* Probably a good reason not to go on */ + fprintf(stderr, + "Master '%s': Error: get settings failed: %s. " + "Aborting\n", + master_ifname, strerror(res)); + goto out; + } + + /* check if master is indeed a master; + * if not then fail any operation + */ + if (!(master_flags.ifr_flags & IFF_MASTER)) { + fprintf(stderr, + "Illegal operation; the specified interface '%s' " + "is not a master. Aborting\n", + master_ifname); + res = 1; + goto out; + } + + /* check if master is up; if not then fail any operation */ + if (!(master_flags.ifr_flags & IFF_UP)) { + fprintf(stderr, + "Illegal operation; the specified master interface " + "'%s' is not up.\n", + master_ifname); + res = 1; + goto out; + } + + /* Only for enslaving */ + if (!opt_c && !opt_d) { + sa_family_t master_family = master_hwaddr.ifr_hwaddr.sa_family; + unsigned char *hwaddr = + (unsigned char *)master_hwaddr.ifr_hwaddr.sa_data; + + /* The family '1' is ARPHRD_ETHER for ethernet. */ + if (master_family != 1 && !opt_f) { + fprintf(stderr, + "Illegal operation: The specified master " + "interface '%s' is not ethernet-like.\n " + "This program is designed to work with " + "ethernet-like network interfaces.\n " + "Use the '-f' option to force the " + "operation.\n", + master_ifname); + res = 1; + goto out; + } + + /* Check master's hw addr */ + for (i = 0; i < 6; i++) { + if (hwaddr[i] != 0) { + hwaddr_set = 1; + break; + } + } + + if (hwaddr_set) { + v_print("current hardware address of master '%s' " + "is %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x, " + "type %d\n", + master_ifname, + hwaddr[0], hwaddr[1], + hwaddr[2], hwaddr[3], + hwaddr[4], hwaddr[5], + master_family); + } + } + + /* Accepts only one slave */ + if (opt_c) { + /* change active slave */ + res = get_slave_flags(slave_ifname); + if (res) { + fprintf(stderr, + "Slave '%s': Error: get flags failed. " + "Aborting\n", + slave_ifname); + goto out; + } + res = change_active(master_ifname, slave_ifname); + if (res) { + fprintf(stderr, + "Master '%s', Slave '%s': Error: " + "Change active failed\n", + master_ifname, slave_ifname); + } + } else { + /* Accept multiple slaves */ + do { + if (opt_d) { + /* detach a slave interface from the master */ + rv = get_slave_flags(slave_ifname); + if (rv) { + /* Can't work with this slave. */ + /* remember the error and skip it*/ + fprintf(stderr, + "Slave '%s': Error: get flags " + "failed. Skipping\n", + slave_ifname); + res = rv; + continue; + } + rv = release(master_ifname, slave_ifname); + if (rv) { + fprintf(stderr, + "Master '%s', Slave '%s': Error: " + "Release failed\n", + master_ifname, slave_ifname); + res = rv; + } + } else { + /* attach a slave interface to the master */ + rv = get_if_settings(slave_ifname, slave_ifra); + if (rv) { + /* Can't work with this slave. */ + /* remember the error and skip it*/ + fprintf(stderr, + "Slave '%s': Error: get " + "settings failed: %s. " + "Skipping\n", + slave_ifname, strerror(rv)); + res = rv; + continue; + } + rv = enslave(master_ifname, slave_ifname); + if (rv) { + fprintf(stderr, + "Master '%s', Slave '%s': Error: " + "Enslave failed\n", + master_ifname, slave_ifname); + res = rv; + } + } + } while ((slave_ifname = *spp++) != NULL); + } + +out: + if (skfd >= 0) { + close(skfd); + } + + return res; +} + +static short mif_flags; + +/* Get the inteface configuration from the kernel. */ +static int if_getconfig(char *ifname) +{ + struct ifreq ifr; + int metric, mtu; /* Parameters of the master interface. */ + struct sockaddr dstaddr, broadaddr, netmask; + unsigned char *hwaddr; + + strcpy(ifr.ifr_name, ifname); + if (ioctl(skfd, SIOCGIFFLAGS, &ifr) < 0) + return -1; + mif_flags = ifr.ifr_flags; + printf("The result of SIOCGIFFLAGS on %s is %x.\n", + ifname, ifr.ifr_flags); + + strcpy(ifr.ifr_name, ifname); + if (ioctl(skfd, SIOCGIFADDR, &ifr) < 0) + return -1; + printf("The result of SIOCGIFADDR is %2.2x.%2.2x.%2.2x.%2.2x.\n", + ifr.ifr_addr.sa_data[0], ifr.ifr_addr.sa_data[1], + ifr.ifr_addr.sa_data[2], ifr.ifr_addr.sa_data[3]); + + strcpy(ifr.ifr_name, ifname); + if (ioctl(skfd, SIOCGIFHWADDR, &ifr) < 0) + return -1; + + /* Gotta convert from 'char' to unsigned for printf(). */ + hwaddr = (unsigned char *)ifr.ifr_hwaddr.sa_data; + printf("The result of SIOCGIFHWADDR is type %d " + "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x.\n", + ifr.ifr_hwaddr.sa_family, hwaddr[0], hwaddr[1], + hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5]); + + strcpy(ifr.ifr_name, ifname); + if (ioctl(skfd, SIOCGIFMETRIC, &ifr) < 0) { + metric = 0; + } else + metric = ifr.ifr_metric; + printf("The result of SIOCGIFMETRIC is %d\n", metric); + + strcpy(ifr.ifr_name, ifname); + if (ioctl(skfd, SIOCGIFMTU, &ifr) < 0) + mtu = 0; + else + mtu = ifr.ifr_mtu; + printf("The result of SIOCGIFMTU is %d\n", mtu); + + strcpy(ifr.ifr_name, ifname); + if (ioctl(skfd, SIOCGIFDSTADDR, &ifr) < 0) { + memset(&dstaddr, 0, sizeof(struct sockaddr)); + } else + dstaddr = ifr.ifr_dstaddr; + + strcpy(ifr.ifr_name, ifname); + if (ioctl(skfd, SIOCGIFBRDADDR, &ifr) < 0) { + memset(&broadaddr, 0, sizeof(struct sockaddr)); + } else + broadaddr = ifr.ifr_broadaddr; + + strcpy(ifr.ifr_name, ifname); + if (ioctl(skfd, SIOCGIFNETMASK, &ifr) < 0) { + memset(&netmask, 0, sizeof(struct sockaddr)); + } else + netmask = ifr.ifr_netmask; + + return 0; +} + +static void if_print(char *ifname) +{ + char buff[1024]; + struct ifconf ifc; + struct ifreq *ifr; + int i; + + if (ifname == (char *)NULL) { + ifc.ifc_len = sizeof(buff); + ifc.ifc_buf = buff; + if (ioctl(skfd, SIOCGIFCONF, &ifc) < 0) { + perror("SIOCGIFCONF failed"); + return; + } + + ifr = ifc.ifc_req; + for (i = ifc.ifc_len / sizeof(struct ifreq); --i >= 0; ifr++) { + if (if_getconfig(ifr->ifr_name) < 0) { + fprintf(stderr, + "%s: unknown interface.\n", + ifr->ifr_name); + continue; + } + + if (((mif_flags & IFF_UP) == 0) && !opt_a) continue; + /*ife_print(&ife);*/ + } + } else { + if (if_getconfig(ifname) < 0) { + fprintf(stderr, + "%s: unknown interface.\n", ifname); + } + } +} + +static int get_drv_info(char *master_ifname) +{ + struct ifreq ifr; + struct ethtool_drvinfo info; + char *endptr; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, master_ifname, IFNAMSIZ); + ifr.ifr_data = (caddr_t)&info; + + info.cmd = ETHTOOL_GDRVINFO; + strncpy(info.driver, "ifenslave", 32); + snprintf(info.fw_version, 32, "%d", BOND_ABI_VERSION); + + if (ioctl(skfd, SIOCETHTOOL, &ifr) < 0) { + if (errno == EOPNOTSUPP) { + goto out; + } + + saved_errno = errno; + v_print("Master '%s': Error: get bonding info failed %s\n", + master_ifname, strerror(saved_errno)); + return 1; + } + + abi_ver = strtoul(info.fw_version, &endptr, 0); + if (*endptr) { + v_print("Master '%s': Error: got invalid string as an ABI " + "version from the bonding module\n", + master_ifname); + return 1; + } + +out: + v_print("ABI ver is %d\n", abi_ver); + + return 0; +} + +static int change_active(char *master_ifname, char *slave_ifname) +{ + struct ifreq ifr; + int res = 0; + + if (!(slave_flags.ifr_flags & IFF_SLAVE)) { + fprintf(stderr, + "Illegal operation: The specified slave interface " + "'%s' is not a slave\n", + slave_ifname); + return 1; + } + + strncpy(ifr.ifr_name, master_ifname, IFNAMSIZ); + strncpy(ifr.ifr_slave, slave_ifname, IFNAMSIZ); + if ((ioctl(skfd, SIOCBONDCHANGEACTIVE, &ifr) < 0) && + (ioctl(skfd, BOND_CHANGE_ACTIVE_OLD, &ifr) < 0)) { + saved_errno = errno; + v_print("Master '%s': Error: SIOCBONDCHANGEACTIVE failed: " + "%s\n", + master_ifname, strerror(saved_errno)); + res = 1; + } + + return res; +} + +static int enslave(char *master_ifname, char *slave_ifname) +{ + struct ifreq ifr; + int res = 0; + + if (slave_flags.ifr_flags & IFF_SLAVE) { + fprintf(stderr, + "Illegal operation: The specified slave interface " + "'%s' is already a slave\n", + slave_ifname); + return 1; + } + + res = set_if_down(slave_ifname, slave_flags.ifr_flags); + if (res) { + fprintf(stderr, + "Slave '%s': Error: bring interface down failed\n", + slave_ifname); + return res; + } + + if (abi_ver < 2) { + /* Older bonding versions would panic if the slave has no IP + * address, so get the IP setting from the master. + */ + set_if_addr(master_ifname, slave_ifname); + } else { + res = clear_if_addr(slave_ifname); + if (res) { + fprintf(stderr, + "Slave '%s': Error: clear address failed\n", + slave_ifname); + return res; + } + } + + if (master_mtu.ifr_mtu != slave_mtu.ifr_mtu) { + res = set_slave_mtu(slave_ifname, master_mtu.ifr_mtu); + if (res) { + fprintf(stderr, + "Slave '%s': Error: set MTU failed\n", + slave_ifname); + return res; + } + } + + if (hwaddr_set) { + /* Master already has an hwaddr + * so set it's hwaddr to the slave + */ + if (abi_ver < 1) { + /* The driver is using an old ABI, so + * the application sets the slave's + * hwaddr + */ + res = set_slave_hwaddr(slave_ifname, + &(master_hwaddr.ifr_hwaddr)); + if (res) { + fprintf(stderr, + "Slave '%s': Error: set hw address " + "failed\n", + slave_ifname); + goto undo_mtu; + } + + /* For old ABI the application needs to bring the + * slave back up + */ + res = set_if_up(slave_ifname, slave_flags.ifr_flags); + if (res) { + fprintf(stderr, + "Slave '%s': Error: bring interface " + "down failed\n", + slave_ifname); + goto undo_slave_mac; + } + } + /* The driver is using a new ABI, + * so the driver takes care of setting + * the slave's hwaddr and bringing + * it up again + */ + } else { + /* No hwaddr for master yet, so + * set the slave's hwaddr to it + */ + if (abi_ver < 1) { + /* For old ABI, the master needs to be + * down before setting its hwaddr + */ + res = set_if_down(master_ifname, master_flags.ifr_flags); + if (res) { + fprintf(stderr, + "Master '%s': Error: bring interface " + "down failed\n", + master_ifname); + goto undo_mtu; + } + } + + res = set_master_hwaddr(master_ifname, + &(slave_hwaddr.ifr_hwaddr)); + if (res) { + fprintf(stderr, + "Master '%s': Error: set hw address " + "failed\n", + master_ifname); + goto undo_mtu; + } + + if (abi_ver < 1) { + /* For old ABI, bring the master + * back up + */ + res = set_if_up(master_ifname, master_flags.ifr_flags); + if (res) { + fprintf(stderr, + "Master '%s': Error: bring interface " + "up failed\n", + master_ifname); + goto undo_master_mac; + } + } + + hwaddr_set = 1; + } + + /* Do the real thing */ + strncpy(ifr.ifr_name, master_ifname, IFNAMSIZ); + strncpy(ifr.ifr_slave, slave_ifname, IFNAMSIZ); + if ((ioctl(skfd, SIOCBONDENSLAVE, &ifr) < 0) && + (ioctl(skfd, BOND_ENSLAVE_OLD, &ifr) < 0)) { + saved_errno = errno; + v_print("Master '%s': Error: SIOCBONDENSLAVE failed: %s\n", + master_ifname, strerror(saved_errno)); + res = 1; + } + + if (res) { + goto undo_master_mac; + } + + return 0; + +/* rollback (best effort) */ +undo_master_mac: + set_master_hwaddr(master_ifname, &(master_hwaddr.ifr_hwaddr)); + hwaddr_set = 0; + goto undo_mtu; +undo_slave_mac: + set_slave_hwaddr(slave_ifname, &(slave_hwaddr.ifr_hwaddr)); +undo_mtu: + set_slave_mtu(slave_ifname, slave_mtu.ifr_mtu); + return res; +} + +static int release(char *master_ifname, char *slave_ifname) +{ + struct ifreq ifr; + int res = 0; + + if (!(slave_flags.ifr_flags & IFF_SLAVE)) { + fprintf(stderr, + "Illegal operation: The specified slave interface " + "'%s' is not a slave\n", + slave_ifname); + return 1; + } + + strncpy(ifr.ifr_name, master_ifname, IFNAMSIZ); + strncpy(ifr.ifr_slave, slave_ifname, IFNAMSIZ); + if ((ioctl(skfd, SIOCBONDRELEASE, &ifr) < 0) && + (ioctl(skfd, BOND_RELEASE_OLD, &ifr) < 0)) { + saved_errno = errno; + v_print("Master '%s': Error: SIOCBONDRELEASE failed: %s\n", + master_ifname, strerror(saved_errno)); + return 1; + } else if (abi_ver < 1) { + /* The driver is using an old ABI, so we'll set the interface + * down to avoid any conflicts due to same MAC/IP + */ + res = set_if_down(slave_ifname, slave_flags.ifr_flags); + if (res) { + fprintf(stderr, + "Slave '%s': Error: bring interface " + "down failed\n", + slave_ifname); + } + } + + /* set to default mtu */ + set_slave_mtu(slave_ifname, 1500); + + return res; +} + +static int get_if_settings(char *ifname, struct dev_ifr ifra[]) +{ + int i; + int res = 0; + + for (i = 0; ifra[i].req_ifr; i++) { + strncpy(ifra[i].req_ifr->ifr_name, ifname, IFNAMSIZ); + res = ioctl(skfd, ifra[i].req_type, ifra[i].req_ifr); + if (res < 0) { + saved_errno = errno; + v_print("Interface '%s': Error: %s failed: %s\n", + ifname, ifra[i].req_name, + strerror(saved_errno)); + + return saved_errno; + } + } + + return 0; +} + +static int get_slave_flags(char *slave_ifname) +{ + int res = 0; + + strncpy(slave_flags.ifr_name, slave_ifname, IFNAMSIZ); + res = ioctl(skfd, SIOCGIFFLAGS, &slave_flags); + if (res < 0) { + saved_errno = errno; + v_print("Slave '%s': Error: SIOCGIFFLAGS failed: %s\n", + slave_ifname, strerror(saved_errno)); + } else { + v_print("Slave %s: flags %04X.\n", + slave_ifname, slave_flags.ifr_flags); + } + + return res; +} + +static int set_master_hwaddr(char *master_ifname, struct sockaddr *hwaddr) +{ + unsigned char *addr = (unsigned char *)hwaddr->sa_data; + struct ifreq ifr; + int res = 0; + + strncpy(ifr.ifr_name, master_ifname, IFNAMSIZ); + memcpy(&(ifr.ifr_hwaddr), hwaddr, sizeof(struct sockaddr)); + res = ioctl(skfd, SIOCSIFHWADDR, &ifr); + if (res < 0) { + saved_errno = errno; + v_print("Master '%s': Error: SIOCSIFHWADDR failed: %s\n", + master_ifname, strerror(saved_errno)); + return res; + } else { + v_print("Master '%s': hardware address set to " + "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x.\n", + master_ifname, addr[0], addr[1], addr[2], + addr[3], addr[4], addr[5]); + } + + return res; +} + +static int set_slave_hwaddr(char *slave_ifname, struct sockaddr *hwaddr) +{ + unsigned char *addr = (unsigned char *)hwaddr->sa_data; + struct ifreq ifr; + int res = 0; + + strncpy(ifr.ifr_name, slave_ifname, IFNAMSIZ); + memcpy(&(ifr.ifr_hwaddr), hwaddr, sizeof(struct sockaddr)); + res = ioctl(skfd, SIOCSIFHWADDR, &ifr); + if (res < 0) { + saved_errno = errno; + + v_print("Slave '%s': Error: SIOCSIFHWADDR failed: %s\n", + slave_ifname, strerror(saved_errno)); + + if (saved_errno == EBUSY) { + v_print(" The device is busy: it must be idle " + "before running this command.\n"); + } else if (saved_errno == EOPNOTSUPP) { + v_print(" The device does not support setting " + "the MAC address.\n" + " Your kernel likely does not support slave " + "devices.\n"); + } else if (saved_errno == EINVAL) { + v_print(" The device's address type does not match " + "the master's address type.\n"); + } + return res; + } else { + v_print("Slave '%s': hardware address set to " + "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x.\n", + slave_ifname, addr[0], addr[1], addr[2], + addr[3], addr[4], addr[5]); + } + + return res; +} + +static int set_slave_mtu(char *slave_ifname, int mtu) +{ + struct ifreq ifr; + int res = 0; + + ifr.ifr_mtu = mtu; + strncpy(ifr.ifr_name, slave_ifname, IFNAMSIZ); + + res = ioctl(skfd, SIOCSIFMTU, &ifr); + if (res < 0) { + saved_errno = errno; + v_print("Slave '%s': Error: SIOCSIFMTU failed: %s\n", + slave_ifname, strerror(saved_errno)); + } else { + v_print("Slave '%s': MTU set to %d.\n", slave_ifname, mtu); + } + + return res; +} + +static int set_if_flags(char *ifname, short flags) +{ + struct ifreq ifr; + int res = 0; + + ifr.ifr_flags = flags; + strncpy(ifr.ifr_name, ifname, IFNAMSIZ); + + res = ioctl(skfd, SIOCSIFFLAGS, &ifr); + if (res < 0) { + saved_errno = errno; + v_print("Interface '%s': Error: SIOCSIFFLAGS failed: %s\n", + ifname, strerror(saved_errno)); + } else { + v_print("Interface '%s': flags set to %04X.\n", ifname, flags); + } + + return res; +} + +static int set_if_up(char *ifname, short flags) +{ + return set_if_flags(ifname, flags | IFF_UP); +} + +static int set_if_down(char *ifname, short flags) +{ + return set_if_flags(ifname, flags & ~IFF_UP); +} + +static int clear_if_addr(char *ifname) +{ + struct ifreq ifr; + int res = 0; + + strncpy(ifr.ifr_name, ifname, IFNAMSIZ); + ifr.ifr_addr.sa_family = AF_INET; + memset(ifr.ifr_addr.sa_data, 0, sizeof(ifr.ifr_addr.sa_data)); + + res = ioctl(skfd, SIOCSIFADDR, &ifr); + if (res < 0) { + saved_errno = errno; + v_print("Interface '%s': Error: SIOCSIFADDR failed: %s\n", + ifname, strerror(saved_errno)); + } else { + v_print("Interface '%s': address cleared\n", ifname); + } + + return res; +} + +static int set_if_addr(char *master_ifname, char *slave_ifname) +{ + struct ifreq ifr; + int res; + unsigned char *ipaddr; + int i; + struct { + char *req_name; + char *desc; + int g_ioctl; + int s_ioctl; + } ifra[] = { + {"IFADDR", "addr", SIOCGIFADDR, SIOCSIFADDR}, + {"DSTADDR", "destination addr", SIOCGIFDSTADDR, SIOCSIFDSTADDR}, + {"BRDADDR", "broadcast addr", SIOCGIFBRDADDR, SIOCSIFBRDADDR}, + {"NETMASK", "netmask", SIOCGIFNETMASK, SIOCSIFNETMASK}, + {NULL, NULL, 0, 0}, + }; + + for (i = 0; ifra[i].req_name; i++) { + strncpy(ifr.ifr_name, master_ifname, IFNAMSIZ); + res = ioctl(skfd, ifra[i].g_ioctl, &ifr); + if (res < 0) { + int saved_errno = errno; + + v_print("Interface '%s': Error: SIOCG%s failed: %s\n", + master_ifname, ifra[i].req_name, + strerror(saved_errno)); + + ifr.ifr_addr.sa_family = AF_INET; + memset(ifr.ifr_addr.sa_data, 0, + sizeof(ifr.ifr_addr.sa_data)); + } + + strncpy(ifr.ifr_name, slave_ifname, IFNAMSIZ); + res = ioctl(skfd, ifra[i].s_ioctl, &ifr); + if (res < 0) { + int saved_errno = errno; + + v_print("Interface '%s': Error: SIOCS%s failed: %s\n", + slave_ifname, ifra[i].req_name, + strerror(saved_errno)); + + } + + ipaddr = (unsigned char *)ifr.ifr_addr.sa_data; + v_print("Interface '%s': set IP %s to %d.%d.%d.%d\n", + slave_ifname, ifra[i].desc, + ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3]); + } + + return 0; +} + +/* + * Local variables: + * version-control: t + * kept-new-versions: 5 + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * compile-command: "gcc -Wall -Wstrict-prototypes -O -I/usr/src/linux/include ifenslave.c -o ifenslave" + * End: + */ + diff --git a/in6_flowlabel.h b/in6_flowlabel.h new file mode 100644 index 0000000..68b58d6 --- /dev/null +++ b/in6_flowlabel.h @@ -0,0 +1,39 @@ +/* + It is just a stripped copy of the kernel header "linux/in6.h" + + "Flow label" things are still not defined in "netinet/in*.h" headers, + but we cannot use "linux/in6.h" immediately because it currently + conflicts with "netinet/in.h" . +*/ + +struct in6_flowlabel_req +{ + struct in6_addr flr_dst; + __u32 flr_label; + __u8 flr_action; + __u8 flr_share; + __u16 flr_flags; + __u16 flr_expires; + __u16 flr_linger; + __u32 __flr_pad; + /* Options in format of IPV6_PKTOPTIONS */ +}; + +#define IPV6_FL_A_GET 0 +#define IPV6_FL_A_PUT 1 +#define IPV6_FL_A_RENEW 2 + +#define IPV6_FL_F_CREATE 1 +#define IPV6_FL_F_EXCL 2 + +#define IPV6_FL_S_NONE 0 +#define IPV6_FL_S_EXCL 1 +#define IPV6_FL_S_PROCESS 2 +#define IPV6_FL_S_USER 3 +#define IPV6_FL_S_ANY 255 + +#define IPV6_FLOWINFO_FLOWLABEL 0x000fffff +#define IPV6_FLOWINFO_PRIORITY 0x0ff00000 + +#define IPV6_FLOWLABEL_MGR 32 +#define IPV6_FLOWINFO_SEND 33 diff --git a/ipg b/ipg new file mode 100755 index 0000000..9332013 --- /dev/null +++ b/ipg @@ -0,0 +1,34 @@ +#! /bin/bash + +if [ -e /proc/modules ] ; then + modprobe pg3 >& /dev/null + modprobe pktgen >& /dev/null +fi + +for PGDEV in /proc/net/pg /proc/net/pktgen/pg0 / ; do + [ -e ${PGDEV} ] && break +done +if [ "${PGDEV}" = "/" ] ; then + echo "Could not locate pg in /proc/net" 1>&2 + exit 1 +fi + +function pgset() { + local result + + echo $1 > ${PGDEV} + + result=`cat ${PGDEV} | fgrep "Result: OK:"` + if [ "$result" = "" ]; then + cat ${PGDEV} | fgrep Result: + fi +} + +function pg() { + echo inject > ${PGDEV} + cat ${PGDEV} +} + +pgset "odev eth0" +pgset "dst 0.0.0.0" + diff --git a/iputils_md5dig.h b/iputils_md5dig.h new file mode 100644 index 0000000..1177491 --- /dev/null +++ b/iputils_md5dig.h @@ -0,0 +1,85 @@ +#ifndef IPUTILS_MD5DIG_H +#define IPUTILS_MD5DIG_H + +#if defined(USE_GCRYPT) +# include +# include +# define IPUTILS_MD5DIG_LEN 16 +#elif defined(USE_NETTLE) +# include +#else +# include +#endif + +#if defined(USE_GCRYPT) +typedef struct { + gcry_md_hd_t dig; +} iputils_md5dig_ctx; + +static void iputils_md5dig_init(iputils_md5dig_ctx *ctx) +{ + if (gcry_md_open(&ctx->dig, GCRY_MD_MD5, 0) != GPG_ERR_NO_ERROR) + abort(); + return; +} + +static void iputils_md5dig_update(iputils_md5dig_ctx *ctx, + void *buf, int len) +{ + gcry_md_write(ctx->dig, buf, len); + return; +} + +static void iputils_md5dig_final(unsigned char *digest, + iputils_md5dig_ctx *ctx) +{ + const void *p; + size_t dlen; + + p = gcry_md_read(ctx->dig, GCRY_MD_MD5); + dlen = gcry_md_get_algo_dlen(GCRY_MD_MD5); + + if (dlen != IPUTILS_MD5DIG_LEN) + abort(); + + memcpy(digest, p, dlen); + + gcry_md_close(ctx->dig); +} + +# define MD5_DIGEST_LENGTH IPUTILS_MD5DIG_LEN +# define MD5_CTX iputils_md5dig_ctx +# define MD5_Init iputils_md5dig_init +# define MD5_Update iputils_md5dig_update +# define MD5_Final iputils_md5dig_final + +#elif defined(USE_NETTLE) +typedef struct md5_ctx iputils_md5dig_ctx; + +static void iputils_md5dig_init(iputils_md5dig_ctx *ctx) +{ + md5_init(ctx); + return; +} + +static void iputils_md5dig_update(iputils_md5dig_ctx *ctx, + void *buf, int len) +{ + md5_update(ctx, len, buf); + return; +} + +static void iputils_md5dig_final(unsigned char *digest, + iputils_md5dig_ctx *ctx) +{ + md5_digest(ctx, MD5_DIGEST_SIZE, digest); +} + +# define MD5_DIGEST_LENGTH MD5_DIGEST_SIZE +# define MD5_CTX iputils_md5dig_ctx +# define MD5_Init iputils_md5dig_init +# define MD5_Update iputils_md5dig_update +# define MD5_Final iputils_md5dig_final +#endif + +#endif diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..a8a9f49 --- /dev/null +++ b/meson.build @@ -0,0 +1,70 @@ +project('iputils', 'c', + version : '20170717') # version hardcoded + +global_cflags = ['-D_GNU_SOURCE', '-O3', '-g', '-fno-strict-aliasing', '-Wstrict-prototypes', '-Wall'] +add_project_arguments(global_cflags, language: 'c') + +message('meson build is EXPERIMENTAL, please DO NOT USE for production!\n') + +cc = meson.get_compiler('c') +m_dep = cc.find_library('m') +resolv_dep = cc.find_library('resolv') +rt_dep = cc.find_library('rt') + +opt = get_option('USE_CAP') +if opt == true + cap_dep = cc.find_library('cap') + add_project_arguments('-DCAPABILITIES', language : 'c') +else + cap_dep = [] +endif + +opt = get_option('USE_IDN') +if opt == '1' + idn_dep = cc.find_library('idn') + add_project_arguments('-DUSE_IDN', language : 'c') +elif opt == '2' + idn_dep = cc.find_library('idn2', required : false) + message('libidn2 is unsupported at this moment.\n') + idn_dep = [] +else + idn_dep = [] +endif + +opt = get_option('USE_CRYPTO') +if opt == 'nettle' + crypto_dep = dependency('nettle') + add_project_arguments('-DUSE_NETTLE', language : 'c') +elif opt == 'gcrypt' + crypto_dep = cc.find_library('gcrypt') + add_project_arguments('-DUSE_GCRYPT', language : 'c') +elif opt == 'openssl' + crypto_dep = dependency('openssl') + add_project_arguments('-DUSE_OPENSSL', language : 'c') +elif opt == 'none' + crypto_dep = [] +endif + +ping_src = ['ping.c', 'ping_common.c', 'ping6_common.c'] +p = executable('ping', ping_src, + dependencies : [m_dep, cap_dep, idn_dep, crypto_dep, resolv_dep]) + +executable('tracepath', 'tracepath.c', + dependencies : idn_dep) + +executable('traceroute6', 'traceroute6.c', + dependencies : [cap_dep, idn_dep]) + +executable('clockdiff', 'clockdiff.c', + dependencies : [cap_dep]) + +executable('rdisc', 'rdisc.c') + +executable('arping', 'arping.c', + dependencies : [rt_dep, cap_dep, idn_dep]) + +executable('tftpd', ['tftpd.c', 'tftpsubs.c']) + +executable('rarpd', 'rarpd.c') + +#test('ping to 127.0.0.1', p, args : ['-p 1', '127.0.0.1']) diff --git a/meson_options.txt b/meson_options.txt new file mode 100644 index 0000000..daee6b6 --- /dev/null +++ b/meson_options.txt @@ -0,0 +1,15 @@ +option('USE_CAP', type : 'boolean', value : true, + description : 'Capatiblity support (with libcap)') +option('USE_IDN', type : 'combo', choices : ['0', '1', '2'], value : '1', + description : 'IDN support') +option('USE_CRYPTO', type : 'combo', choices : ['none', 'nettle', 'gcrypt','openssl'], value : 'nettle', + description: 'Crypto library support for ping 6. You can choose between none, Nettle, GCrypt or openssl/libressl') + +#option('ARPING_DEFAULT_DEVICE', type : 'string', value : '', +# description : 'default device for arping') + +#option('someoption', type : 'string', value : 'optval', description : 'An option') +#option('other_one', type : 'boolean', value : false) +#option('combo_opt', type : 'combo', choices : ['one', 'two', 'three'], value : 'three') + + diff --git a/ninfod/COPYING b/ninfod/COPYING new file mode 100644 index 0000000..30df58e --- /dev/null +++ b/ninfod/COPYING @@ -0,0 +1,26 @@ +Copyright (C) 2002 USAGI/WIDE Project. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. Neither the name of the project nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. diff --git a/ninfod/Makefile.in b/ninfod/Makefile.in new file mode 100644 index 0000000..ebd7838 --- /dev/null +++ b/ninfod/Makefile.in @@ -0,0 +1,88 @@ +# $USAGI: Makefile.in,v 1.6 2003-01-15 06:41:23 mk Exp $ +# +# Copyright (C)2002 USAGI/WIDE Project. +# Copyright (C)2000-2001 Hideaki YOSHIFUJI and USAGI Project. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the project nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +# $Id: Makefile.in,v 1.2 2000/06/10 05:45:14 yoshfuji Exp yoshfuji $ + +SHELL = @SHELL@ + +srcdir = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datarootdir = @datarootdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ + +INSTALL = @INSTALL@ + +CC = @CC@ +CFLAGS=@CFLAGS@ -D_GNU_SOURCE +DEFS=@DEFS@ +LIBS=@LIBS@ +LDFLAGS=@LDFLAGS@ +INSTALL = @INSTALL@ + +# ---------------- +all: ninfod +clean: + -rm -f *.o ninfod +distclean: clean + -rm -f *~ *.bak #* + -rm -fr autom4te.cache + -rm -f Makefile config.h config.cache config.status config.log + -rm -f ninfod.sh +install: all + @INSTALL_DIR@ @sbindir@ + @INSTALL@ ninfod -o root @sbindir@ + @INSTALL@ ninfod.sh -o root -m 755 @sysconfdir@/init.d/ninfod + +# ---------------- +ninfod: ninfod_addrs.o ni_ifaddrs.o ninfod_name.o ninfod_core.o ninfod.o + $(CC) $(LDFLAGS) $^ $(LIBS) -o $@ +%.o: %.c + $(CC) $(CFLAGS) -c $(DEFS) -o $@ $< + +# ---------------- +ni_ifaddrs.o: config.h ni_ifaddrs.h +ninfod.o: config.h ninfod.h +ninfod_addrs.c: config.h ninfod.h ni_ifaddrs.h +ninfod_core.c: config.h ninfod.h +ninfod_name.c: config.h ninfod.h + diff --git a/ninfod/config.h.in b/ninfod/config.h.in new file mode 100644 index 0000000..87ee886 --- /dev/null +++ b/ninfod/config.h.in @@ -0,0 +1,142 @@ +/* config.h.in. Generated from configure.in by autoheader. */ + +/* Define if building universal (internal helper macro) */ +#undef AC_APPLE_UNIVERSAL_BUILD + +/* Enable debugging */ +#undef ENABLE_DEBUG + +/* Enable ttl support for qtypes (deprecated) */ +#undef ENABLE_SUPTYPES + +/* Enable threads */ +#undef ENABLE_THREADS + +/* Define to 1 if you have the header file. */ +#undef HAVE_ARPA_INET_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_GCRYPT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_GNUTLS_OPENSSL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the `cap' library (-lcap). */ +#undef HAVE_LIBCAP + +/* Define to 1 if you have the `pthread' library (-lpthread). */ +#undef HAVE_LIBPTHREAD + +/* Define to 1 if you have the header file. */ +#undef HAVE_LIMITS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_LINUX_RTNETLINK_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the `nanosleep' function. */ +#undef HAVE_NANOSLEEP + +/* Define to 1 if you have the header file. */ +#undef HAVE_NETDB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NETINET_ICMP6_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NETINET_IN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NETINET_IP6_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_OPENSSL_MD5_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_PTHREAD_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_PWD_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have struct icmp6_nodeinfo */ +#undef HAVE_STRUCT_ICMP6_NODEINFO + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYSLOG_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_CAPABILITY_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_UIO_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_UTSNAME_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Define to 1 if you can safely include both and . */ +#undef TIME_WITH_SYS_TIME + +/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most + significant byte first (like Motorola and SPARC, unlike Intel). */ +#if defined AC_APPLE_UNIVERSAL_BUILD +# if defined __BIG_ENDIAN__ +# define WORDS_BIGENDIAN 1 +# endif +#else +# ifndef WORDS_BIGENDIAN +# undef WORDS_BIGENDIAN +# endif +#endif + +/* Define to empty if `const' does not conform to ANSI C. */ +#undef const + +/* Define to `unsigned int' if does not define. */ +#undef size_t diff --git a/ninfod/configure b/ninfod/configure new file mode 100755 index 0000000..2b8f24f --- /dev/null +++ b/ninfod/configure @@ -0,0 +1,5539 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.69. +# +# +# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. +# +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +# +# Copyright (C)2002 USAGI/WIDE Project. All Rights Reserved. +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +# Use a proper internal environment variable to ensure we don't fall + # into an infinite loop, continuously re-executing ourselves. + if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then + _as_can_reexec=no; export _as_can_reexec; + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +as_fn_exit 255 + fi + # We don't want this to propagate to other subprocesses. + { _as_can_reexec=; unset _as_can_reexec;} +if test "x$CONFIG_SHELL" = x; then + as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which + # is contrary to our usage. Disable this feature. + alias -g '\${1+\"\$@\"}'='\"\$@\"' + setopt NO_GLOB_SUBST +else + case \`(set -o) 2>/dev/null\` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi +" + as_required="as_fn_return () { (exit \$1); } +as_fn_success () { as_fn_return 0; } +as_fn_failure () { as_fn_return 1; } +as_fn_ret_success () { return 0; } +as_fn_ret_failure () { return 1; } + +exitcode=0 +as_fn_success || { exitcode=1; echo as_fn_success failed.; } +as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } +as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } +as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } +if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : + +else + exitcode=1; echo positional parameters were not saved. +fi +test x\$exitcode = x0 || exit 1 +test -x / || exit 1" + as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO + as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO + eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && + test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 +test \$(( 1 + 1 )) = 2 || exit 1" + if (eval "$as_required") 2>/dev/null; then : + as_have_required=yes +else + as_have_required=no +fi + if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : + +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +as_found=false +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + as_found=: + case $as_dir in #( + /*) + for as_base in sh bash ksh sh5; do + # Try only shells that exist, to save several forks. + as_shell=$as_dir/$as_base + if { test -f "$as_shell" || test -f "$as_shell.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : + CONFIG_SHELL=$as_shell as_have_required=yes + if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : + break 2 +fi +fi + done;; + esac + as_found=false +done +$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : + CONFIG_SHELL=$SHELL as_have_required=yes +fi; } +IFS=$as_save_IFS + + + if test "x$CONFIG_SHELL" != x; then : + export CONFIG_SHELL + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +exit 255 +fi + + if test x$as_have_required = xno; then : + $as_echo "$0: This script requires a shell more modern than all" + $as_echo "$0: the shells that I found on your system." + if test x${ZSH_VERSION+set} = xset ; then + $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" + $as_echo "$0: be upgraded to zsh 4.3.4 or later." + else + $as_echo "$0: Please tell bug-autoconf@gnu.org about your system, +$0: including any error possibly output before this +$0: message. Then install a modern shell, or manually run +$0: the script under such a shell if you do have one." + fi + exit 1 +fi +fi +fi +SHELL=${CONFIG_SHELL-/bin/sh} +export SHELL +# Unset more variables known to interfere with behavior of common tools. +CLICOLOR_FORCE= GREP_OPTIONS= +unset CLICOLOR_FORCE GREP_OPTIONS + +## --------------------- ## +## M4sh Shell Functions. ## +## --------------------- ## +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + + + as_lineno_1=$LINENO as_lineno_1a=$LINENO + as_lineno_2=$LINENO as_lineno_2a=$LINENO + eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && + test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { + # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } + + # If we had to re-execute with $CONFIG_SHELL, we're ensured to have + # already done that, so ensure we don't try to do so again and fall + # in an infinite loop. This has already happened in practice. + _as_can_reexec=no; export _as_can_reexec + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +test -n "$DJDIR" || exec 7<&0 &1 + +# Name of the host. +# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_clean_files= +ac_config_libobj_dir=. +LIBOBJS= +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= + +# Identity of this package. +PACKAGE_NAME= +PACKAGE_TARNAME= +PACKAGE_VERSION= +PACKAGE_STRING= +PACKAGE_BUGREPORT= +PACKAGE_URL= + +ac_unique_file="ninfod.c" +ac_default_prefix=/usr/local/v6 +# Factoring default headers for most tests. +ac_includes_default="\ +#include +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef STDC_HEADERS +# include +# include +#else +# ifdef HAVE_STDLIB_H +# include +# endif +#endif +#ifdef HAVE_STRING_H +# if !defined STDC_HEADERS && defined HAVE_MEMORY_H +# include +# endif +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_INTTYPES_H +# include +#endif +#ifdef HAVE_STDINT_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif" + +ac_subst_vars='LTLIBOBJS +LIBOBJS +EGREP +GREP +CPP +INSTALL_DIR +INSTALL_LIB +INSTALL_DATA +INSTALL_SCRIPT +INSTALL_PROGRAM +OBJEXT +EXEEXT +ac_ct_CC +CPPFLAGS +LDFLAGS +CFLAGS +CC +target_alias +host_alias +build_alias +LIBS +ECHO_T +ECHO_N +ECHO_C +DEFS +mandir +localedir +libdir +psdir +pdfdir +dvidir +htmldir +infodir +docdir +oldincludedir +includedir +localstatedir +sharedstatedir +sysconfdir +datadir +datarootdir +libexecdir +sbindir +bindir +program_transform_name +prefix +exec_prefix +PACKAGE_URL +PACKAGE_BUGREPORT +PACKAGE_STRING +PACKAGE_VERSION +PACKAGE_TARNAME +PACKAGE_NAME +PATH_SEPARATOR +SHELL' +ac_subst_files='' +ac_user_opts=' +enable_option_checking +enable_debug +enable_threads +enable_suptypes +' + ac_precious_vars='build_alias +host_alias +target_alias +CC +CFLAGS +LDFLAGS +LIBS +CPPFLAGS +CPP' + + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +ac_unrecognized_opts= +ac_unrecognized_sep= +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +# (The list follows the same order as the GNU Coding Standards.) +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datarootdir='${prefix}/share' +datadir='${datarootdir}' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +includedir='${prefix}/include' +oldincludedir='/usr/include' +docdir='${datarootdir}/doc/${PACKAGE}' +infodir='${datarootdir}/info' +htmldir='${docdir}' +dvidir='${docdir}' +pdfdir='${docdir}' +psdir='${docdir}' +libdir='${exec_prefix}/lib' +localedir='${datarootdir}/locale' +mandir='${datarootdir}/man' + +ac_prev= +ac_dashdash= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval $ac_prev=\$ac_option + ac_prev= + continue + fi + + case $ac_option in + *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; + *=) ac_optarg= ;; + *) ac_optarg=yes ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_dashdash$ac_option in + --) + ac_dashdash=yes ;; + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=*) + datadir=$ac_optarg ;; + + -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ + | --dataroo | --dataro | --datar) + ac_prev=datarootdir ;; + -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ + | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) + datarootdir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=no ;; + + -docdir | --docdir | --docdi | --doc | --do) + ac_prev=docdir ;; + -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) + docdir=$ac_optarg ;; + + -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) + ac_prev=dvidir ;; + -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) + dvidir=$ac_optarg ;; + + -enable-* | --enable-*) + ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=\$ac_optarg ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) + ac_prev=htmldir ;; + -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ + | --ht=*) + htmldir=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localedir | --localedir | --localedi | --localed | --locale) + ac_prev=localedir ;; + -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) + localedir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst | --locals) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) + ac_prev=pdfdir ;; + -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) + pdfdir=$ac_optarg ;; + + -psdir | --psdir | --psdi | --psd | --ps) + ac_prev=psdir ;; + -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) + psdir=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=\$ac_optarg ;; + + -without-* | --without-*) + ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=no ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) as_fn_error $? "unrecognized option: \`$ac_option' +Try \`$0 --help' for more information" + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + case $ac_envvar in #( + '' | [0-9]* | *[!_$as_cr_alnum]* ) + as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; + esac + eval $ac_envvar=\$ac_optarg + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + as_fn_error $? "missing argument to $ac_option" +fi + +if test -n "$ac_unrecognized_opts"; then + case $enable_option_checking in + no) ;; + fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; + *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; + esac +fi + +# Check all directory arguments for consistency. +for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ + datadir sysconfdir sharedstatedir localstatedir includedir \ + oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ + libdir localedir mandir +do + eval ac_val=\$$ac_var + # Remove trailing slashes. + case $ac_val in + */ ) + ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` + eval $ac_var=\$ac_val;; + esac + # Be sure to have absolute directory names. + case $ac_val in + [\\/$]* | ?:[\\/]* ) continue;; + NONE | '' ) case $ac_var in *prefix ) continue;; esac;; + esac + as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +ac_pwd=`pwd` && test -n "$ac_pwd" && +ac_ls_di=`ls -di .` && +ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || + as_fn_error $? "working directory cannot be determined" +test "X$ac_ls_di" = "X$ac_pwd_ls_di" || + as_fn_error $? "pwd does not report name of working directory" + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then the parent directory. + ac_confdir=`$as_dirname -- "$as_myself" || +$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_myself" : 'X\(//\)[^/]' \| \ + X"$as_myself" : 'X\(//\)$' \| \ + X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_myself" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r "$srcdir/$ac_unique_file"; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r "$srcdir/$ac_unique_file"; then + test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." + as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" +fi +ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" +ac_abs_confdir=`( + cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" + pwd)` +# When building in place, set srcdir=. +if test "$ac_abs_confdir" = "$ac_pwd"; then + srcdir=. +fi +# Remove unnecessary trailing slashes from srcdir. +# Double slashes in file names in object file debugging info +# mess up M-x gdb in Emacs. +case $srcdir in +*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; +esac +for ac_var in $ac_precious_vars; do + eval ac_env_${ac_var}_set=\${${ac_var}+set} + eval ac_env_${ac_var}_value=\$${ac_var} + eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} + eval ac_cv_env_${ac_var}_value=\$${ac_var} +done + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures this package to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking ...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] + --datadir=DIR read-only architecture-independent data [DATAROOTDIR] + --infodir=DIR info documentation [DATAROOTDIR/info] + --localedir=DIR locale-dependent data [DATAROOTDIR/locale] + --mandir=DIR man documentation [DATAROOTDIR/man] + --docdir=DIR documentation root [DATAROOTDIR/doc/PACKAGE] + --htmldir=DIR html documentation [DOCDIR] + --dvidir=DIR dvi documentation [DOCDIR] + --pdfdir=DIR pdf documentation [DOCDIR] + --psdir=DIR ps documentation [DOCDIR] +_ACEOF + + cat <<\_ACEOF +_ACEOF +fi + +if test -n "$ac_init_help"; then + + cat <<\_ACEOF + +Optional Features: + --disable-option-checking ignore unrecognized --enable/--with options + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --enable-debug Enable debugging + --disable-threads Disable threads (and random delay) + --enable-suptypes Enable suptypes qtype (deprecated) + --enable-ttl Enable ttl support for qtypes (deprecated) + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L if you have libraries in a + nonstandard directory + LIBS libraries to pass to the linker, e.g. -l + CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if + you have headers in a nonstandard directory + CPP C preprocessor + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +Report bugs to the package provider. +_ACEOF +ac_status=$? +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d "$ac_dir" || + { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || + continue + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + cd "$ac_dir" || { ac_status=$?; continue; } + # Check for guested configure. + if test -f "$ac_srcdir/configure.gnu"; then + echo && + $SHELL "$ac_srcdir/configure.gnu" --help=recursive + elif test -f "$ac_srcdir/configure"; then + echo && + $SHELL "$ac_srcdir/configure" --help=recursive + else + $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi || ac_status=$? + cd "$ac_pwd" || { ac_status=$?; break; } + done +fi + +test -n "$ac_init_help" && exit $ac_status +if $ac_init_version; then + cat <<\_ACEOF +configure +generated by GNU Autoconf 2.69 + +Copyright (C) 2012 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. + +Copyright (C)2002 USAGI/WIDE Project. All Rights Reserved. +_ACEOF + exit +fi + +## ------------------------ ## +## Autoconf initialization. ## +## ------------------------ ## + +# ac_fn_c_try_compile LINENO +# -------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_compile + +# ac_fn_c_try_cpp LINENO +# ---------------------- +# Try to preprocess conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_cpp () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } > conftest.i && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_cpp + +# ac_fn_c_try_run LINENO +# ---------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes +# that executables *can* be run. +ac_fn_c_try_run () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then : + ac_retval=0 +else + $as_echo "$as_me: program exited with status $ac_status" >&5 + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=$ac_status +fi + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_run + +# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists, giving a warning if it cannot be compiled using +# the include files in INCLUDES and setting the cache variable VAR +# accordingly. +ac_fn_c_check_header_mongrel () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if eval \${$3+:} false; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +else + # Is the header compilable? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 +$as_echo_n "checking $2 usability... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_header_compiler=yes +else + ac_header_compiler=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 +$as_echo "$ac_header_compiler" >&6; } + +# Is the header present? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 +$as_echo_n "checking $2 presence... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <$2> +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + ac_header_preproc=yes +else + ac_header_preproc=no +fi +rm -f conftest.err conftest.i conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 +$as_echo "$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( + yes:no: ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 +$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} + ;; + no:yes:* ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 +$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 +$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 +$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 +$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} + ;; +esac + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=\$ac_header_compiler" +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_mongrel + +# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists and can be compiled using the include files in +# INCLUDES, setting the cache variable VAR accordingly. +ac_fn_c_check_header_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_compile + +# ac_fn_c_check_type LINENO TYPE VAR INCLUDES +# ------------------------------------------- +# Tests whether TYPE exists after having included INCLUDES, setting cache +# variable VAR accordingly. +ac_fn_c_check_type () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=no" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +if (sizeof ($2)) + return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +if (sizeof (($2))) + return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + eval "$3=yes" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_type + +# ac_fn_c_try_link LINENO +# ----------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_link () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest$ac_exeext + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + test -x conftest$ac_exeext + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information + # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would + # interfere with the next link command; also delete a directory that is + # left behind by Apple's compiler. We do this before executing the actions. + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_link + +# ac_fn_c_check_func LINENO FUNC VAR +# ---------------------------------- +# Tests whether FUNC exists, setting the cache variable VAR accordingly +ac_fn_c_check_func () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +/* Define $2 to an innocuous variant, in case declares $2. + For example, HP-UX 11i declares gettimeofday. */ +#define $2 innocuous_$2 + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $2 (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $2 + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $2 (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$2 || defined __stub___$2 +choke me +#endif + +int +main () +{ +return $2 (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_func +cat >config.log <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by $as_me, which was +generated by GNU Autoconf 2.69. Invocation command line was + + $ $0 $@ + +_ACEOF +exec 5>>config.log +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + $as_echo "PATH: $as_dir" + done +IFS=$as_save_IFS + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *\'*) + ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; + 2) + as_fn_append ac_configure_args1 " '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + as_fn_append ac_configure_args " '$ac_arg'" + ;; + esac + done +done +{ ac_configure_args0=; unset ac_configure_args0;} +{ ac_configure_args1=; unset ac_configure_args1;} + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Use '\'' to represent an apostrophe within the trap. +# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + $as_echo "## ---------------- ## +## Cache variables. ## +## ---------------- ##" + echo + # The following way of writing the cache mishandles newlines in values, +( + for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + (set) 2>&1 | + case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + sed -n \ + "s/'\''/'\''\\\\'\'''\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" + ;; #( + *) + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) + echo + + $as_echo "## ----------------- ## +## Output variables. ## +## ----------------- ##" + echo + for ac_var in $ac_subst_vars + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + + if test -n "$ac_subst_files"; then + $as_echo "## ------------------- ## +## File substitutions. ## +## ------------------- ##" + echo + for ac_var in $ac_subst_files + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + fi + + if test -s confdefs.h; then + $as_echo "## ----------- ## +## confdefs.h. ## +## ----------- ##" + echo + cat confdefs.h + echo + fi + test "$ac_signal" != 0 && + $as_echo "$as_me: caught signal $ac_signal" + $as_echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core core.conftest.* && + rm -f -r conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status +' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -f -r conftest* confdefs.h + +$as_echo "/* confdefs.h */" > confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_URL "$PACKAGE_URL" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer an explicitly selected file to automatically selected ones. +ac_site_file1=NONE +ac_site_file2=NONE +if test -n "$CONFIG_SITE"; then + # We do not want a PATH search for config.site. + case $CONFIG_SITE in #(( + -*) ac_site_file1=./$CONFIG_SITE;; + */*) ac_site_file1=$CONFIG_SITE;; + *) ac_site_file1=./$CONFIG_SITE;; + esac +elif test "x$prefix" != xNONE; then + ac_site_file1=$prefix/share/config.site + ac_site_file2=$prefix/etc/config.site +else + ac_site_file1=$ac_default_prefix/share/config.site + ac_site_file2=$ac_default_prefix/etc/config.site +fi +for ac_site_file in "$ac_site_file1" "$ac_site_file2" +do + test "x$ac_site_file" = xNONE && continue + if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 +$as_echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" \ + || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "failed to load site script $ac_site_file +See \`config.log' for more details" "$LINENO" 5; } + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special files + # actually), so we avoid doing that. DJGPP emulates it as a regular file. + if test /dev/null != "$cache_file" && test -f "$cache_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 +$as_echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . "$cache_file";; + *) . "./$cache_file";; + esac + fi +else + { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 +$as_echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in $ac_precious_vars; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val=\$ac_cv_env_${ac_var}_value + eval ac_new_val=\$ac_env_${ac_var}_value + case $ac_old_set,$ac_new_set in + set,) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + # differences in whitespace do not lead to failure. + ac_old_val_w=`echo x $ac_old_val` + ac_new_val_w=`echo x $ac_new_val` + if test "$ac_old_val_w" != "$ac_new_val_w"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 +$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + ac_cache_corrupted=: + else + { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 +$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} + eval $ac_var=\$ac_old_val + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 +$as_echo "$as_me: former value: \`$ac_old_val'" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 +$as_echo "$as_me: current value: \`$ac_new_val'" >&2;} + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) as_fn_append ac_configure_args " '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 +$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} + as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 +fi +## -------------------- ## +## Main body of script. ## +## -------------------- ## + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +ac_config_headers="$ac_config_headers config.h" + + + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "no acceptable C compiler found in \$PATH +See \`config.log' for more details" "$LINENO" 5; } + +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 +$as_echo_n "checking whether the C compiler works... " >&6; } +ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` + +# The possible output files: +ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" + +ac_rmfiles= +for ac_file in $ac_files +do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + * ) ac_rmfiles="$ac_rmfiles $ac_file";; + esac +done +rm -f $ac_rmfiles + +if { { ac_try="$ac_link_default" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link_default") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. +# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' +# in a Makefile. We should not override ac_cv_exeext if it was cached, +# so that the user can short-circuit this test for compilers unknown to +# Autoconf. +for ac_file in $ac_files '' +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; + then :; else + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + fi + # We set ac_cv_exeext here because the later test for it is not + # safe: cross compilers may not add the suffix if given an `-o' + # argument, so we may need to know it at that point already. + # Even if this section looks crufty: it has the advantage of + # actually working. + break;; + * ) + break;; + esac +done +test "$ac_cv_exeext" = no && ac_cv_exeext= + +else + ac_file='' +fi +if test -z "$ac_file"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "C compiler cannot create executables +See \`config.log' for more details" "$LINENO" 5; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 +$as_echo_n "checking for C compiler default output file name... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 +$as_echo "$ac_file" >&6; } +ac_exeext=$ac_cv_exeext + +rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 +$as_echo_n "checking for suffix of executables... " >&6; } +if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + break;; + * ) break;; + esac +done +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest conftest$ac_cv_exeext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 +$as_echo "$ac_cv_exeext" >&6; } + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +FILE *f = fopen ("conftest.out", "w"); + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} +_ACEOF +ac_clean_files="$ac_clean_files conftest.out" +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 +$as_echo_n "checking whether we are cross compiling... " >&6; } +if test "$cross_compiling" != yes; then + { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if { ac_try='./conftest$ac_cv_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details" "$LINENO" 5; } + fi + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 +$as_echo "$cross_compiling" >&6; } + +rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 +$as_echo_n "checking for suffix of object files... " >&6; } +if ${ac_cv_objext+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + for ac_file in conftest.o conftest.obj conftest.*; do + test -f "$ac_file" || continue; + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of object files: cannot compile +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 +$as_echo "$ac_cv_objext" >&6; } +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if ${ac_cv_c_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if ${ac_cv_prog_cc_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +else + CFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if ${ac_cv_prog_cc_c89+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +struct stat; +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_c89=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; +esac +if test "x$ac_cv_prog_cc_c89" != xno; then : + +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +ac_aux_dir= +for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do + if test -f "$ac_dir/install-sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f "$ac_dir/install.sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f "$ac_dir/shtool"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5 +fi + +# These three variables are undocumented and unsupported, +# and are intended to be withdrawn in a future Autoconf release. +# They can cause serious problems if a builder's source tree is in a directory +# whose full name contains unusual characters. +ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. +ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. +ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. + + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +# Reject install programs that cannot install multiple files. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 +$as_echo_n "checking for a BSD-compatible install... " >&6; } +if test -z "$INSTALL"; then +if ${ac_cv_path_install+:} false; then : + $as_echo_n "(cached) " >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in #(( + ./ | .// | /[cC]/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then + if test $ac_prog = install && + grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + rm -rf conftest.one conftest.two conftest.dir + echo one > conftest.one + echo two > conftest.two + mkdir conftest.dir + if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && + test -s conftest.one && test -s conftest.two && + test -s conftest.dir/conftest.one && + test -s conftest.dir/conftest.two + then + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + fi + done + done + ;; +esac + + done +IFS=$as_save_IFS + +rm -rf conftest.one conftest.two conftest.dir + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. Don't cache a + # value for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + INSTALL=$ac_install_sh + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 +$as_echo "$INSTALL" >&6; } + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +INSTALL_LIB="\${INSTALL_DATA}" + +INSTALL_DIR="\${INSTALL} -d" + + +# Check whether --enable-debug was given. +if test "${enable_debug+set}" = set; then : + enableval=$enable_debug; +fi + +if test x"$enableval" != x"no"; then + +$as_echo "#define ENABLE_DEBUG 1" >>confdefs.h + +fi + +# Check whether --enable-threads was given. +if test "${enable_threads+set}" = set; then : + enableval=$enable_threads; +else + enable_threads=no +fi + +if test x"$enableval" != x"no"; then + +$as_echo "#define ENABLE_THREADS 1" >>confdefs.h + +fi + +# Check whether --enable-suptypes was given. +if test "${enable_suptypes+set}" = set; then : + enableval=$enable_suptypes; +fi + +if test x"$enableval" != x"no"; then + +$as_echo "#define ENABLE_SUPTYPES 1" >>confdefs.h + +fi + +# Check whether --enable-suptypes was given. +if test "${enable_suptypes+set}" = set; then : + enableval=$enable_suptypes; +fi + +if test x"$enableval" != x"no"; then + +$as_echo "#define ENABLE_SUPTYPES 1" >>confdefs.h + +fi + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 +$as_echo_n "checking how to run the C preprocessor... " >&6; } +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if ${ac_cv_prog_CPP+:} false; then : + $as_echo_n "(cached) " >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 +$as_echo "$CPP" >&6; } +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details" "$LINENO" 5; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 +$as_echo_n "checking for grep that handles long lines and -e... " >&6; } +if ${ac_cv_path_GREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$GREP"; then + ac_path_GREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in grep ggrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_GREP" || continue +# Check for GNU ac_path_GREP and select it if it is found. + # Check for GNU $ac_path_GREP +case `"$ac_path_GREP" --version 2>&1` in +*GNU*) + ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'GREP' >> "conftest.nl" + "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_GREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_GREP="$ac_path_GREP" + ac_path_GREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_GREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_GREP"; then + as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_GREP=$GREP +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 +$as_echo "$ac_cv_path_GREP" >&6; } + GREP="$ac_cv_path_GREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 +$as_echo_n "checking for egrep... " >&6; } +if ${ac_cv_path_EGREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 + then ac_cv_path_EGREP="$GREP -E" + else + if test -z "$EGREP"; then + ac_path_EGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in egrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_EGREP" || continue +# Check for GNU ac_path_EGREP and select it if it is found. + # Check for GNU $ac_path_EGREP +case `"$ac_path_EGREP" --version 2>&1` in +*GNU*) + ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'EGREP' >> "conftest.nl" + "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_EGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_EGREP="$ac_path_EGREP" + ac_path_EGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_EGREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_EGREP"; then + as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_EGREP=$EGREP +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 +$as_echo "$ac_cv_path_EGREP" >&6; } + EGREP="$ac_cv_path_EGREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 +$as_echo_n "checking for ANSI C header files... " >&6; } +if ${ac_cv_header_stdc+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_stdc=yes +else + ac_cv_header_stdc=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then : + : +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + return 2; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + +else + ac_cv_header_stdc=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 +$as_echo "$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then + +$as_echo "#define STDC_HEADERS 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether time.h and sys/time.h may both be included" >&5 +$as_echo_n "checking whether time.h and sys/time.h may both be included... " >&6; } +if ${ac_cv_header_time+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#include + +int +main () +{ +if ((struct tm *) 0) +return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_time=yes +else + ac_cv_header_time=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_time" >&5 +$as_echo "$ac_cv_header_time" >&6; } +if test $ac_cv_header_time = yes; then + +$as_echo "#define TIME_WITH_SYS_TIME 1" >>confdefs.h + +fi + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default +" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +for ac_header in limits.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "limits.h" "ac_cv_header_limits_h" "$ac_includes_default" +if test "x$ac_cv_header_limits_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIMITS_H 1 +_ACEOF + +fi + +done + +for ac_header in gcrypt.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "gcrypt.h" "ac_cv_header_gcrypt_h" "$ac_includes_default" +if test "x$ac_cv_header_gcrypt_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_GCRYPT_H 1 +_ACEOF + +fi + +done + +for ac_header in gnutls/openssl.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "gnutls/openssl.h" "ac_cv_header_gnutls_openssl_h" "$ac_includes_default" +if test "x$ac_cv_header_gnutls_openssl_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_GNUTLS_OPENSSL_H 1 +_ACEOF + +fi + +done + +for ac_header in openssl/md5.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "openssl/md5.h" "ac_cv_header_openssl_md5_h" "$ac_includes_default" +if test "x$ac_cv_header_openssl_md5_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_OPENSSL_MD5_H 1 +_ACEOF + +fi + +done + +for ac_header in sys/uio.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "sys/uio.h" "ac_cv_header_sys_uio_h" "$ac_includes_default" +if test "x$ac_cv_header_sys_uio_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_SYS_UIO_H 1 +_ACEOF + +fi + +done + +for ac_header in sys/utsname.h arpa/inet.h netdb.h syslog.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + +for ac_header in sys/capability.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "sys/capability.h" "ac_cv_header_sys_capability_h" "$ac_includes_default" +if test "x$ac_cv_header_sys_capability_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_SYS_CAPABILITY_H 1 +_ACEOF + +fi + +done + +for ac_header in pwd.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "pwd.h" "ac_cv_header_pwd_h" "$ac_includes_default" +if test "x$ac_cv_header_pwd_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_PWD_H 1 +_ACEOF + +fi + +done + +for ac_header in netinet/in.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "netinet/in.h" "ac_cv_header_netinet_in_h" "$ac_includes_default" +if test "x$ac_cv_header_netinet_in_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_NETINET_IN_H 1 +_ACEOF + +fi + +done + +for ac_header in netinet/ip6.h netinet/icmp6.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" " +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_NETINET_IN_H +# include +#endif + +" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + +for ac_header in linux/rtnetlink.h +do : + ac_fn_c_check_header_compile "$LINENO" "linux/rtnetlink.h" "ac_cv_header_linux_rtnetlink_h" " +#include +#include + +" +if test "x$ac_cv_header_linux_rtnetlink_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LINUX_RTNETLINK_H 1 +_ACEOF + +fi + +done + +for ac_header in pthread.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "pthread.h" "ac_cv_header_pthread_h" "$ac_includes_default" +if test "x$ac_cv_header_pthread_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_PTHREAD_H 1 +_ACEOF + +fi + +done + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether byte ordering is bigendian" >&5 +$as_echo_n "checking whether byte ordering is bigendian... " >&6; } +if ${ac_cv_c_bigendian+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_c_bigendian=unknown + # See if we're dealing with a universal compiler. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifndef __APPLE_CC__ + not a universal capable compiler + #endif + typedef int dummy; + +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + + # Check for potential -arch flags. It is not universal unless + # there are at least two -arch flags with different values. + ac_arch= + ac_prev= + for ac_word in $CC $CFLAGS $CPPFLAGS $LDFLAGS; do + if test -n "$ac_prev"; then + case $ac_word in + i?86 | x86_64 | ppc | ppc64) + if test -z "$ac_arch" || test "$ac_arch" = "$ac_word"; then + ac_arch=$ac_word + else + ac_cv_c_bigendian=universal + break + fi + ;; + esac + ac_prev= + elif test "x$ac_word" = "x-arch"; then + ac_prev=arch + fi + done +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + if test $ac_cv_c_bigendian = unknown; then + # See if sys/param.h defines the BYTE_ORDER macro. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + #include + +int +main () +{ +#if ! (defined BYTE_ORDER && defined BIG_ENDIAN \ + && defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \ + && LITTLE_ENDIAN) + bogus endian macros + #endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + # It does; now see whether it defined to BIG_ENDIAN or not. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + #include + +int +main () +{ +#if BYTE_ORDER != BIG_ENDIAN + not big endian + #endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_c_bigendian=yes +else + ac_cv_c_bigendian=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + fi + if test $ac_cv_c_bigendian = unknown; then + # See if defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris). + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +int +main () +{ +#if ! (defined _LITTLE_ENDIAN || defined _BIG_ENDIAN) + bogus endian macros + #endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + # It does; now see whether it defined to _BIG_ENDIAN or not. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +int +main () +{ +#ifndef _BIG_ENDIAN + not big endian + #endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_c_bigendian=yes +else + ac_cv_c_bigendian=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + fi + if test $ac_cv_c_bigendian = unknown; then + # Compile a test program. + if test "$cross_compiling" = yes; then : + # Try to guess by grepping values from an object file. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +short int ascii_mm[] = + { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 }; + short int ascii_ii[] = + { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 }; + int use_ascii (int i) { + return ascii_mm[i] + ascii_ii[i]; + } + short int ebcdic_ii[] = + { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 }; + short int ebcdic_mm[] = + { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 }; + int use_ebcdic (int i) { + return ebcdic_mm[i] + ebcdic_ii[i]; + } + extern int foo; + +int +main () +{ +return use_ascii (foo) == use_ebcdic (foo); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + if grep BIGenDianSyS conftest.$ac_objext >/dev/null; then + ac_cv_c_bigendian=yes + fi + if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then + if test "$ac_cv_c_bigendian" = unknown; then + ac_cv_c_bigendian=no + else + # finding both strings is unlikely to happen, but who knows? + ac_cv_c_bigendian=unknown + fi + fi +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ + + /* Are we little or big endian? From Harbison&Steele. */ + union + { + long int l; + char c[sizeof (long int)]; + } u; + u.l = 1; + return u.c[sizeof (long int) - 1] == 1; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + ac_cv_c_bigendian=no +else + ac_cv_c_bigendian=yes +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_bigendian" >&5 +$as_echo "$ac_cv_c_bigendian" >&6; } + case $ac_cv_c_bigendian in #( + yes) + $as_echo "#define WORDS_BIGENDIAN 1" >>confdefs.h +;; #( + no) + ;; #( + universal) + +$as_echo "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h + + ;; #( + *) + as_fn_error $? "unknown endianness + presetting ac_cv_c_bigendian=no (or yes) will help" "$LINENO" 5 ;; + esac + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for an ANSI C-conforming const" >&5 +$as_echo_n "checking for an ANSI C-conforming const... " >&6; } +if ${ac_cv_c_const+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + +#ifndef __cplusplus + /* Ultrix mips cc rejects this sort of thing. */ + typedef int charset[2]; + const charset cs = { 0, 0 }; + /* SunOS 4.1.1 cc rejects this. */ + char const *const *pcpcc; + char **ppc; + /* NEC SVR4.0.2 mips cc rejects this. */ + struct point {int x, y;}; + static struct point const zero = {0,0}; + /* AIX XL C 1.02.0.0 rejects this. + It does not let you subtract one const X* pointer from another in + an arm of an if-expression whose if-part is not a constant + expression */ + const char *g = "string"; + pcpcc = &g + (g ? g-g : 0); + /* HPUX 7.0 cc rejects these. */ + ++pcpcc; + ppc = (char**) pcpcc; + pcpcc = (char const *const *) ppc; + { /* SCO 3.2v4 cc rejects this sort of thing. */ + char tx; + char *t = &tx; + char const *s = 0 ? (char *) 0 : (char const *) 0; + + *t++ = 0; + if (s) return 0; + } + { /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */ + int x[] = {25, 17}; + const int *foo = &x[0]; + ++foo; + } + { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */ + typedef const int *iptr; + iptr p = 0; + ++p; + } + { /* AIX XL C 1.02.0.0 rejects this sort of thing, saying + "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ + struct s { int j; const int *ap[3]; } bx; + struct s *b = &bx; b->j = 5; + } + { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ + const int foo = 10; + if (!foo) return 0; + } + return !cs[0] && !zero.x; +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_c_const=yes +else + ac_cv_c_const=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_const" >&5 +$as_echo "$ac_cv_c_const" >&6; } +if test $ac_cv_c_const = no; then + +$as_echo "#define const /**/" >>confdefs.h + +fi + +ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default" +if test "x$ac_cv_type_size_t" = xyes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define size_t unsigned int +_ACEOF + +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for struct icmp6_nodeinfo" >&5 +$as_echo_n "checking for struct icmp6_nodeinfo... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#include +#include + +int +main () +{ + +struct icmp6_nodeinfo nodeinfo; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + +$as_echo "#define HAVE_STRUCT_ICMP6_NODEINFO 1" >>confdefs.h + + +else + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +for ac_func in nanosleep +do : + ac_fn_c_check_func "$LINENO" "nanosleep" "ac_cv_func_nanosleep" +if test "x$ac_cv_func_nanosleep" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_NANOSLEEP 1 +_ACEOF + +fi +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_create in -lpthread" >&5 +$as_echo_n "checking for pthread_create in -lpthread... " >&6; } +if ${ac_cv_lib_pthread_pthread_create+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lpthread $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char pthread_create (); +int +main () +{ +return pthread_create (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_pthread_pthread_create=yes +else + ac_cv_lib_pthread_pthread_create=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthread_pthread_create" >&5 +$as_echo "$ac_cv_lib_pthread_pthread_create" >&6; } +if test "x$ac_cv_lib_pthread_pthread_create" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBPTHREAD 1 +_ACEOF + + LIBS="-lpthread $LIBS" + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for cap_init in -lcap" >&5 +$as_echo_n "checking for cap_init in -lcap... " >&6; } +if ${ac_cv_lib_cap_cap_init+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lcap $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char cap_init (); +int +main () +{ +return cap_init (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_cap_cap_init=yes +else + ac_cv_lib_cap_cap_init=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_cap_cap_init" >&5 +$as_echo "$ac_cv_lib_cap_cap_init" >&6; } +if test "x$ac_cv_lib_cap_cap_init" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBCAP 1 +_ACEOF + + LIBS="-lcap $LIBS" + +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for MD5_Init in -lcrypto" >&5 +$as_echo_n "checking for MD5_Init in -lcrypto... " >&6; } +if ${ac_cv_lib_crypto_MD5_Init+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lcrypto $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char MD5_Init (); +int +main () +{ +return MD5_Init (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_crypto_MD5_Init=yes +else + ac_cv_lib_crypto_MD5_Init=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_crypto_MD5_Init" >&5 +$as_echo "$ac_cv_lib_crypto_MD5_Init" >&6; } +if test "x$ac_cv_lib_crypto_MD5_Init" = xyes; then : + $as_echo "#define HAVE_MD5_INIT 1" >>confdefs.h + + LIBS="-lcrypto $LIBS" + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for gcry_md_open in -lgcrypt" >&5 +$as_echo_n "checking for gcry_md_open in -lgcrypt... " >&6; } +if ${ac_cv_lib_gcrypt_gcry_md_open+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lgcrypt $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char gcry_md_open (); +int +main () +{ +return gcry_md_open (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_gcrypt_gcry_md_open=yes +else + ac_cv_lib_gcrypt_gcry_md_open=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_gcrypt_gcry_md_open" >&5 +$as_echo "$ac_cv_lib_gcrypt_gcry_md_open" >&6; } +if test "x$ac_cv_lib_gcrypt_gcry_md_open" = xyes; then : + +$as_echo "#define HAVE_GCRY_MD_OPEN /**/" >>confdefs.h + + LIBS="-lgcrypt $LIBS" + +fi + + + + +ac_config_files="$ac_config_files Makefile ninfod.sh" + +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, we kill variables containing newlines. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +( + for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + + (set) 2>&1 | + case $as_nl`(ac_space=' '; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + # `set' does not quote correctly, so add quotes: double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \. + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; #( + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) | + sed ' + /^ac_cv_env_/b end + t clear + :clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + :end' >>confcache +if diff "$cache_file" confcache >/dev/null 2>&1; then :; else + if test -w "$cache_file"; then + if test "x$cache_file" != "x/dev/null"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 +$as_echo "$as_me: updating cache $cache_file" >&6;} + if test ! -f "$cache_file" || test -h "$cache_file"; then + cat confcache >"$cache_file" + else + case $cache_file in #( + */* | ?:*) + mv -f confcache "$cache_file"$$ && + mv -f "$cache_file"$$ "$cache_file" ;; #( + *) + mv -f confcache "$cache_file" ;; + esac + fi + fi + else + { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 +$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +DEFS=-DHAVE_CONFIG_H + +ac_libobjs= +ac_ltlibobjs= +U= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' + ac_i=`$as_echo "$ac_i" | sed "$ac_script"` + # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR + # will be set to the directory where LIBOBJS objects are built. + as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" + as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + + + +: "${CONFIG_STATUS=./config.status}" +ac_write_fail=0 +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 +$as_echo "$as_me: creating $CONFIG_STATUS" >&6;} +as_write_fail=0 +cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false + +SHELL=\${CONFIG_SHELL-$SHELL} +export SHELL +_ASEOF +cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 6>&1 +## ----------------------------------- ## +## Main body of $CONFIG_STATUS script. ## +## ----------------------------------- ## +_ASEOF +test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# Save the log message, to keep $0 and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. +ac_log=" +This file was extended by $as_me, which was +generated by GNU Autoconf 2.69. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" + +_ACEOF + +case $ac_config_files in *" +"*) set x $ac_config_files; shift; ac_config_files=$*;; +esac + +case $ac_config_headers in *" +"*) set x $ac_config_headers; shift; ac_config_headers=$*;; +esac + + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# Files that config.status was made for. +config_files="$ac_config_files" +config_headers="$ac_config_headers" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +ac_cs_usage="\ +\`$as_me' instantiates files and other configuration actions +from templates according to the current configuration. Unless the files +and actions are specified as TAGs, all are instantiated by default. + +Usage: $0 [OPTION]... [TAG]... + + -h, --help print this help, then exit + -V, --version print version number and configuration settings, then exit + --config print configuration, then exit + -q, --quiet, --silent + do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Report bugs to the package provider." + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" +ac_cs_version="\\ +config.status +configured by $0, generated by GNU Autoconf 2.69, + with options \\"\$ac_cs_config\\" + +Copyright (C) 2012 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." + +ac_pwd='$ac_pwd' +srcdir='$srcdir' +INSTALL='$INSTALL' +test -n "\$AWK" || AWK=awk +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# The default lists apply if the user does not specify any file. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=?*) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` + ac_shift=: + ;; + --*=) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg= + ac_shift=: + ;; + *) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + esac + + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + $as_echo "$ac_cs_version"; exit ;; + --config | --confi | --conf | --con | --co | --c ) + $as_echo "$ac_cs_config"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + '') as_fn_error $? "missing file argument" ;; + esac + as_fn_append CONFIG_FILES " '$ac_optarg'" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + as_fn_append CONFIG_HEADERS " '$ac_optarg'" + ac_need_defaults=false;; + --he | --h) + # Conflict between --help and --header + as_fn_error $? "ambiguous option: \`$1' +Try \`$0 --help' for more information.";; + --help | --hel | -h ) + $as_echo "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) as_fn_error $? "unrecognized option: \`$1' +Try \`$0 --help' for more information." ;; + + *) as_fn_append ac_config_targets " $1" + ac_need_defaults=false ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +if \$ac_cs_recheck; then + set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion + shift + \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 + CONFIG_SHELL='$SHELL' + export CONFIG_SHELL + exec "\$@" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + $as_echo "$ac_log" +} >&5 + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; + "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; + "ninfod.sh") CONFIG_FILES="$CONFIG_FILES ninfod.sh" ;; + + *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; + esac +done + + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= ac_tmp= + trap 'exit_status=$? + : "${ac_tmp:=$tmp}" + { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status +' 0 + trap 'as_fn_exit 1' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 +ac_tmp=$tmp + +# Set up the scripts for CONFIG_FILES section. +# No need to generate them if there are no CONFIG_FILES. +# This happens for instance with `./config.status config.h'. +if test -n "$CONFIG_FILES"; then + + +ac_cr=`echo X | tr X '\015'` +# On cygwin, bash can eat \r inside `` if the user requested igncr. +# But we know of no other shell where ac_cr would be empty at this +# point, so we can use a bashism as a fallback. +if test "x$ac_cr" = x; then + eval ac_cr=\$\'\\r\' +fi +ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` +if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then + ac_cs_awk_cr='\\r' +else + ac_cs_awk_cr=$ac_cr +fi + +echo 'BEGIN {' >"$ac_tmp/subs1.awk" && +_ACEOF + + +{ + echo "cat >conf$$subs.awk <<_ACEOF" && + echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && + echo "_ACEOF" +} >conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 +ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` +ac_delim='%!_!# ' +for ac_last_try in false false false false false :; do + . ./conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + + ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` + if test $ac_delim_n = $ac_delim_num; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done +rm -f conf$$subs.sh + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && +_ACEOF +sed -n ' +h +s/^/S["/; s/!.*/"]=/ +p +g +s/^[^!]*!// +:repl +t repl +s/'"$ac_delim"'$// +t delim +:nl +h +s/\(.\{148\}\)..*/\1/ +t more1 +s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ +p +n +b repl +:more1 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t nl +:delim +h +s/\(.\{148\}\)..*/\1/ +t more2 +s/["\\]/\\&/g; s/^/"/; s/$/"/ +p +b +:more2 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t delim +' >$CONFIG_STATUS || ac_write_fail=1 +rm -f conf$$subs.awk +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACAWK +cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && + for (key in S) S_is_set[key] = 1 + FS = "" + +} +{ + line = $ 0 + nfields = split(line, field, "@") + substed = 0 + len = length(field[1]) + for (i = 2; i < nfields; i++) { + key = field[i] + keylen = length(key) + if (S_is_set[key]) { + value = S[key] + line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) + len += length(value) + length(field[++i]) + substed = 1 + } else + len += 1 + keylen + } + + print line +} + +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then + sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" +else + cat +fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ + || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 +_ACEOF + +# VPATH may cause trouble with some makes, so we remove sole $(srcdir), +# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ +h +s/// +s/^/:/ +s/[ ]*$/:/ +s/:\$(srcdir):/:/g +s/:\${srcdir}:/:/g +s/:@srcdir@:/:/g +s/^:*// +s/:*$// +x +s/\(=[ ]*\).*/\1/ +G +s/\n// +s/^[^=]*=[ ]*$// +}' +fi + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +fi # test -n "$CONFIG_FILES" + +# Set up the scripts for CONFIG_HEADERS section. +# No need to generate them if there are no CONFIG_HEADERS. +# This happens for instance with `./config.status Makefile'. +if test -n "$CONFIG_HEADERS"; then +cat >"$ac_tmp/defines.awk" <<\_ACAWK || +BEGIN { +_ACEOF + +# Transform confdefs.h into an awk script `defines.awk', embedded as +# here-document in config.status, that substitutes the proper values into +# config.h.in to produce config.h. + +# Create a delimiter string that does not exist in confdefs.h, to ease +# handling of long lines. +ac_delim='%!_!# ' +for ac_last_try in false false :; do + ac_tt=`sed -n "/$ac_delim/p" confdefs.h` + if test -z "$ac_tt"; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done + +# For the awk script, D is an array of macro values keyed by name, +# likewise P contains macro parameters if any. Preserve backslash +# newline sequences. + +ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* +sed -n ' +s/.\{148\}/&'"$ac_delim"'/g +t rset +:rset +s/^[ ]*#[ ]*define[ ][ ]*/ / +t def +d +:def +s/\\$// +t bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3"/p +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p +d +:bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3\\\\\\n"\\/p +t cont +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p +t cont +d +:cont +n +s/.\{148\}/&'"$ac_delim"'/g +t clear +:clear +s/\\$// +t bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/"/p +d +:bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p +b cont +' >$CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + for (key in D) D_is_set[key] = 1 + FS = "" +} +/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { + line = \$ 0 + split(line, arg, " ") + if (arg[1] == "#") { + defundef = arg[2] + mac1 = arg[3] + } else { + defundef = substr(arg[1], 2) + mac1 = arg[2] + } + split(mac1, mac2, "(") #) + macro = mac2[1] + prefix = substr(line, 1, index(line, defundef) - 1) + if (D_is_set[macro]) { + # Preserve the white space surrounding the "#". + print prefix "define", macro P[macro] D[macro] + next + } else { + # Replace #undef with comments. This is necessary, for example, + # in the case of _POSIX_SOURCE, which is predefined and required + # on some systems where configure will not decide to define it. + if (defundef == "undef") { + print "/*", prefix defundef, macro, "*/" + next + } + } +} +{ print } +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 +fi # test -n "$CONFIG_HEADERS" + + +eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS " +shift +for ac_tag +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$ac_tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; + esac + case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac + as_fn_append ac_file_inputs " '$ac_f'" + done + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + configure_input='Generated from '` + $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' + `' by configure.' + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 +$as_echo "$as_me: creating $ac_file" >&6;} + fi + # Neutralize special characters interpreted by sed in replacement strings. + case $configure_input in #( + *\&* | *\|* | *\\* ) + ac_sed_conf_input=`$as_echo "$configure_input" | + sed 's/[\\\\&|]/\\\\&/g'`;; #( + *) ac_sed_conf_input=$configure_input;; + esac + + case $ac_tag in + *:-:* | *:-) cat >"$ac_tmp/stdin" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir="$ac_dir"; as_fn_mkdir_p + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; + esac +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= +ac_sed_dataroot=' +/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p' +case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + ac_datarootdir_hack=' + s&@datadir@&$datadir&g + s&@docdir@&$docdir&g + s&@infodir@&$infodir&g + s&@localedir@&$localedir&g + s&@mandir@&$mandir&g + s&\\\${datarootdir}&$datarootdir&g' ;; +esac +_ACEOF + +# Neutralize VPATH when `$srcdir' = `.'. +# Shell code in configure.ac might set extrasub. +# FIXME: do we really want to maintain this feature? +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_sed_extra="$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s|@configure_input@|$ac_sed_conf_input|;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@top_build_prefix@&$ac_top_build_prefix&;t t +s&@srcdir@&$ac_srcdir&;t t +s&@abs_srcdir@&$ac_abs_srcdir&;t t +s&@top_srcdir@&$ac_top_srcdir&;t t +s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t +s&@builddir@&$ac_builddir&;t t +s&@abs_builddir@&$ac_abs_builddir&;t t +s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +s&@INSTALL@&$ac_INSTALL&;t t +$ac_datarootdir_hack +" +eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ + >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ + "$ac_tmp/out"`; test -z "$ac_out"; } && + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&5 +$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&2;} + + rm -f "$ac_tmp/stdin" + case $ac_file in + -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; + *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; + esac \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + ;; + :H) + # + # CONFIG_HEADER + # + if test x"$ac_file" != x-; then + { + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" + } >"$ac_tmp/config.h" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then + { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 +$as_echo "$as_me: $ac_file is unchanged" >&6;} + else + rm -f "$ac_file" + mv "$ac_tmp/config.h" "$ac_file" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + fi + else + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ + || as_fn_error $? "could not create -" "$LINENO" 5 + fi + ;; + + + esac + +done # for ac_tag + + +as_fn_exit 0 +_ACEOF +ac_clean_files=$ac_clean_files_save + +test $ac_write_fail = 0 || + as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || as_fn_exit 1 +fi +if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 +$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} +fi + diff --git a/ninfod/configure.in b/ninfod/configure.in new file mode 100644 index 0000000..ee5ce9d --- /dev/null +++ b/ninfod/configure.in @@ -0,0 +1,143 @@ +dnl $USAGI: configure.in,v 1.12 2003-07-16 09:49:01 yoshfuji Exp $ + +dnl Copyright (C) 2002 USAGI/WIDE Project. +dnl All rights reserved. +dnl +dnl Redistribution and use in source and binary forms, with or without +dnl modification, are permitted provided that the following conditions +dnl are met: +dnl 1. Redistributions of source code must retain the above copyright +dnl notice, this list of conditions and the following disclaimer. +dnl 2. Redistributions in binary form must reproduce the above copyright +dnl notice, this list of conditions and the following disclaimer in the +dnl documentation and/or other materials provided with the distribution. +dnl 3. Neither the name of the project nor the names of its contributors +dnl may be used to endorse or promote products derived from this software +dnl without specific prior written permission. +dnl +dnl THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND +dnl ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +dnl IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +dnl ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE +dnl FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +dnl DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +dnl OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +dnl HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +dnl LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +dnl OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +dnl SUCH DAMAGE. + +AC_PREREQ(2.53) +AC_INIT(ninfod.c) +AC_CONFIG_HEADER(config.h) +AC_PREFIX_DEFAULT(/usr/local/v6) + +AC_COPYRIGHT([Copyright (C)2002 USAGI/WIDE Project. All Rights Reserved.]) + +dnl Checks for programs. +AC_PROG_CC +AC_PROG_INSTALL +INSTALL_LIB="\${INSTALL_DATA}" +AC_SUBST(INSTALL_LIB) +INSTALL_DIR="\${INSTALL} -d" +AC_SUBST(INSTALL_DIR) + +dnl Checks for Enable/With +AC_ARG_ENABLE(debug, +[ --enable-debug Enable debugging]) +if test x"$enableval" != x"no"; then + AC_DEFINE(ENABLE_DEBUG, 1, + [Enable debugging]) +fi + +AC_ARG_ENABLE(threads, +[ --disable-threads Disable threads (and random delay)],,enable_threads=no) +if test x"$enableval" != x"no"; then + AC_DEFINE(ENABLE_THREADS, 1, + [Enable threads]) +fi + +AC_ARG_ENABLE(suptypes, +[ --enable-suptypes Enable suptypes qtype (deprecated)]) +if test x"$enableval" != x"no"; then + AC_DEFINE(ENABLE_SUPTYPES, 1, + [Enable suptypes (deprecated)]) +fi + +AC_ARG_ENABLE(suptypes, +[ --enable-ttl Enable ttl support for qtypes (deprecated)]) +if test x"$enableval" != x"no"; then + AC_DEFINE(ENABLE_SUPTYPES, 1, + [Enable ttl support for qtypes (deprecated)]) +fi + +dnl Checks for libraries. + +dnl Checks for header files. +AC_HEADER_STDC +AC_HEADER_TIME +AC_CHECK_HEADERS(limits.h) +AC_CHECK_HEADERS(gcrypt.h) +AC_CHECK_HEADERS(gnutls/openssl.h) +AC_CHECK_HEADERS(openssl/md5.h) +AC_CHECK_HEADERS(sys/uio.h) +AC_CHECK_HEADERS(sys/utsname.h arpa/inet.h netdb.h syslog.h) +AC_CHECK_HEADERS(sys/capability.h) +AC_CHECK_HEADERS(pwd.h) +AC_CHECK_HEADERS(netinet/in.h) +AC_CHECK_HEADERS(netinet/ip6.h netinet/icmp6.h,,,[ +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_NETINET_IN_H +# include +#endif +]) +AC_CHECK_HEADERS(linux/rtnetlink.h,,,[ +#include +#include +]) +AC_CHECK_HEADERS(pthread.h) + +dnl Checks for typedefs, structures, and compiler characteristics. +AC_C_BIGENDIAN +AC_C_CONST +AC_TYPE_SIZE_T + +AC_MSG_CHECKING([for struct icmp6_nodeinfo]) +AC_TRY_COMPILE([ +#include +#include +#include +],[ +struct icmp6_nodeinfo nodeinfo; +],[ + AC_MSG_RESULT([yes]) + AC_DEFINE([HAVE_STRUCT_ICMP6_NODEINFO], 1, + [Define to 1 if you have struct icmp6_nodeinfo]) +],[ + AC_MSG_RESULT([no]) +]) + +dnl Checks for library functions. +AC_CHECK_FUNCS(nanosleep) +AC_CHECK_LIB(pthread, pthread_create) +AC_CHECK_LIB(cap, cap_init) + +AC_CHECK_LIB(crypto, MD5_Init, + AC_DEFINE(HAVE_MD5_INIT) + LIBS="-lcrypto $LIBS" +) +AC_CHECK_LIB(gcrypt, gcry_md_open, + AC_DEFINE(HAVE_GCRY_MD_OPEN,[],[if you have gcrypt]) + LIBS="-lgcrypt $LIBS" +) + +dnl AC_CHECK_LIB(crypto, MD5Init, +dnl AC_DEFINE(HAVE_MD5INIT) +dnl LIBS="-lcrypto $LIBS", +dnl) + +dnl AC_SUBST(DEFS) + +AC_OUTPUT(Makefile ninfod.sh) diff --git a/ninfod/icmp6_nodeinfo.h b/ninfod/icmp6_nodeinfo.h new file mode 100644 index 0000000..5c6e741 --- /dev/null +++ b/ninfod/icmp6_nodeinfo.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2002 USAGI/WIDE Project. + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef ICMP6_NODEINFO_H +#define ICMP6_NODEINFO_H + +struct icmp6_nodeinfo { + struct icmp6_hdr icmp6_ni_hdr; + uint8_t icmp6_ni_nonce[8]; + /* could be followed by reply data */ +}; + +#define ni_type icmp6_ni_hdr.icmp6_type +#define ni_code icmp6_ni_hdr.icmp6_code +#define ni_cksum icmp6_ni_hdr.icmp6_cksum +#define ni_qtype icmp6_ni_hdr.icmp6_data16[0] +#define ni_flags icmp6_ni_hdr.icmp6_data16[1] +#define ni_nonce icmp6_ni_nonce + +/* ICMP6 types */ +#define ICMP6_NI_QUERY 139 +#define ICMP6_NI_REPLY 140 + +/* ICMP6 codes for NI Query */ +#define ICMP6_NI_SUBJ_IPV6 0 /* Query Subject is an ipv6 address */ +#define ICMP6_NI_SUBJ_FQDN 1 /* Query Subject is a Domain name */ +#define ICMP6_NI_SUBJ_IPV4 2 /* Query Subject is an ipv4 address */ + +/* ICMP6 codes for NI Reply */ +#define ICMP6_NI_SUCCESS 0 /* NI successful reply */ +#define ICMP6_NI_REFUSED 1 /* NI request is refused */ +#define ICMP6_NI_UNKNOWN 2 /* unknown Qtype */ + +/* NI Codes */ +#define NI_QTYPE_NOOP 0 /* NOOP */ +#define NI_QTYPE_SUPTYPES 1 /* Supported Qtypes */ +#define NI_QTYPE_DNSNAME 2 /* DNS Name */ +#define NI_QTYPE_NODEADDR 3 /* Node Addresses */ +#define NI_QTYPE_IPV4ADDR 4 /* IPv4 Addresses */ + +/* NI Flags */ +#if WORDS_BIGENDIAN +#define NI_SUPTYPE_FLAG_COMPRESS 0x1 +#define NI_FQDN_FLAG_VALIDTTL 0x1 +#else +#define NI_SUPTYPE_FLAG_COMPRESS 0x0100 +#define NI_FQDN_FLAG_VALIDTTL 0x0100 +#endif + +#if WORDS_BIGENDIAN +#define NI_NODEADDR_FLAG_TRUNCATE 0x1 +#define NI_NODEADDR_FLAG_ALL 0x2 +#define NI_NODEADDR_FLAG_COMPAT 0x4 +#define NI_NODEADDR_FLAG_LINKLOCAL 0x8 +#define NI_NODEADDR_FLAG_SITELOCAL 0x10 +#define NI_NODEADDR_FLAG_GLOBAL 0x20 +#else +#define NI_NODEADDR_FLAG_TRUNCATE 0x0100 +#define NI_NODEADDR_FLAG_ALL 0x0200 +#define NI_NODEADDR_FLAG_COMPAT 0x0400 +#define NI_NODEADDR_FLAG_LINKLOCAL 0x0800 +#define NI_NODEADDR_FLAG_SITELOCAL 0x1000 +#define NI_NODEADDR_FLAG_GLOBAL 0x2000 +#endif + +#define NI_IPV4ADDR_FLAG_TRUNCATE NI_NODEADDR_FLAG_TRUNCATE +#define NI_IPV4ADDR_FLAG_ALL NI_NODEADDR_FLAG_ALL + +#endif + diff --git a/ninfod/install-sh b/ninfod/install-sh new file mode 100755 index 0000000..e9de238 --- /dev/null +++ b/ninfod/install-sh @@ -0,0 +1,251 @@ +#!/bin/sh +# +# install - install a program, script, or datafile +# This comes from X11R5 (mit/util/scripts/install.sh). +# +# Copyright 1991 by the Massachusetts Institute of Technology +# +# Permission to use, copy, modify, distribute, and sell this software and its +# documentation for any purpose is hereby granted without fee, provided that +# the above copyright notice appear in all copies and that both that +# copyright notice and this permission notice appear in supporting +# documentation, and that the name of M.I.T. not be used in advertising or +# publicity pertaining to distribution of the software without specific, +# written prior permission. M.I.T. makes no representations about the +# suitability of this software for any purpose. It is provided "as is" +# without express or implied warranty. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. It can only install one file at a time, a restriction +# shared with many OS's install programs. + + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +transformbasename="" +transform_arg="" +instcmd="$mvprog" +chmodcmd="$chmodprog 0755" +chowncmd="" +chgrpcmd="" +stripcmd="" +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src="" +dst="" +dir_arg="" + +while [ x"$1" != x ]; do + case $1 in + -c) instcmd="$cpprog" + shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + -s) stripcmd="$stripprog" + shift + continue;; + + -t=*) transformarg=`echo $1 | sed 's/-t=//'` + shift + continue;; + + -b=*) transformbasename=`echo $1 | sed 's/-b=//'` + shift + continue;; + + *) if [ x"$src" = x ] + then + src=$1 + else + # this colon is to work around a 386BSD /bin/sh bug + : + dst=$1 + fi + shift + continue;; + esac +done + +if [ x"$src" = x ] +then + echo "install: no input file specified" + exit 1 +else + true +fi + +if [ x"$dir_arg" != x ]; then + dst=$src + src="" + + if [ -d $dst ]; then + instcmd=: + chmodcmd="" + else + instcmd=mkdir + fi +else + +# Waiting for this to be detected by the "$instcmd $src $dsttmp" command +# might cause directories to be created, which would be especially bad +# if $src (and thus $dsttmp) contains '*'. + + if [ -f $src -o -d $src ] + then + true + else + echo "install: $src does not exist" + exit 1 + fi + + if [ x"$dst" = x ] + then + echo "install: no destination specified" + exit 1 + else + true + fi + +# If destination is a directory, append the input filename; if your system +# does not like double slashes in filenames, you may need to add some logic + + if [ -d $dst ] + then + dst="$dst"/`basename $src` + else + true + fi +fi + +## this sed command emulates the dirname command +dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` + +# Make sure that the destination directory exists. +# this part is taken from Noah Friedman's mkinstalldirs script + +# Skip lots of stat calls in the usual case. +if [ ! -d "$dstdir" ]; then +defaultIFS=' +' +IFS="${IFS-${defaultIFS}}" + +oIFS="${IFS}" +# Some sh's can't handle IFS=/ for some reason. +IFS='%' +set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` +IFS="${oIFS}" + +pathcomp='' + +while [ $# -ne 0 ] ; do + pathcomp="${pathcomp}${1}" + shift + + if [ ! -d "${pathcomp}" ] ; + then + $mkdirprog "${pathcomp}" + else + true + fi + + pathcomp="${pathcomp}/" +done +fi + +if [ x"$dir_arg" != x ] +then + $doit $instcmd $dst && + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi +else + +# If we're going to rename the final executable, determine the name now. + + if [ x"$transformarg" = x ] + then + dstfile=`basename $dst` + else + dstfile=`basename $dst $transformbasename | + sed $transformarg`$transformbasename + fi + +# don't allow the sed command to completely eliminate the filename + + if [ x"$dstfile" = x ] + then + dstfile=`basename $dst` + else + true + fi + +# Make a temp file name in the proper directory. + + dsttmp=$dstdir/#inst.$$# + +# Move or copy the file name to the temp name + + $doit $instcmd $src $dsttmp && + + trap "rm -f ${dsttmp}" 0 && + +# and set any options; do chmod last to preserve setuid bits + +# If any of these fail, we abort the whole thing. If we want to +# ignore errors from any of these, just make sure not to ignore +# errors from the above "$doit $instcmd $src $dsttmp" command. + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && + +# Now rename the file to the real destination. + + $doit $rmcmd -f $dstdir/$dstfile && + $doit $mvcmd $dsttmp $dstdir/$dstfile + +fi && + + +exit 0 diff --git a/ninfod/iputils_md5dig.h b/ninfod/iputils_md5dig.h new file mode 100644 index 0000000..4cec866 --- /dev/null +++ b/ninfod/iputils_md5dig.h @@ -0,0 +1,52 @@ +#ifndef IPUTILS_MD5DIG_H +#define IPUTILS_MD5DIG_H + +#ifdef USE_GCRYPT +# include +# include +# define IPUTILS_MD5DIG_LEN 16 +#else +# include +#endif + +#ifdef USE_GCRYPT +typedef struct { + gcry_md_hd_t dig; +} iputils_md5dig_ctx; + +static void iputils_md5dig_init(iputils_md5dig_ctx *ctx) +{ + if (gcry_md_open(&ctx->dig, GCRY_MD_MD5, 0) != GPG_ERR_NO_ERROR) + abort(); +} + +static void iputils_md5dig_update(iputils_md5dig_ctx *ctx, + const void *buf, int len) +{ + gcry_md_write(ctx->dig, buf, len); +} + +static void iputils_md5dig_final(unsigned char *digest, + iputils_md5dig_ctx *ctx) +{ + const void *p; + size_t dlen; + + p = gcry_md_read(ctx->dig, GCRY_MD_MD5); + dlen = gcry_md_get_algo_dlen(GCRY_MD_MD5); + + if (dlen != IPUTILS_MD5DIG_LEN) + abort(); + + memcpy(digest, p, dlen); + + gcry_md_close(ctx->dig); +} + +# define MD5_DIGEST_LENGTH IPUTILS_MD5DIG_LEN +# define MD5_CTX iputils_md5dig_ctx +# define MD5_Init iputils_md5dig_init +# define MD5_Update iputils_md5dig_update +# define MD5_Final iputils_md5dig_final +#endif +#endif diff --git a/ninfod/ni_ifaddrs.c b/ninfod/ni_ifaddrs.c new file mode 100644 index 0000000..4134e28 --- /dev/null +++ b/ninfod/ni_ifaddrs.c @@ -0,0 +1,537 @@ +/* $USAGI: ni_ifaddrs.c,v 1.8 2007-10-11 06:25:21 yoshfuji Exp $ */ +/* + * Copyright (C) 2002 USAGI/WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* reformatted by indent -kr -i8 -l 1000 */ +/* USAGI: ifaddrs.c,v 1.18 2002/03/06 01:50:46 yoshfuji Exp */ + +/************************************************************************** + * ifaddrs.c + * Copyright (C)2000 Hideaki YOSHIFUJI, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include /* the L2 protocols */ +#include +#include +#include +#include "ni_ifaddrs.h" +#include + +#ifdef _USAGI_LIBINET6 +#include "libc-compat.h" +#endif + +//#define IFA_LOCAL IFA_LOCAL + +static const char *RCSID __attribute__ ((unused)) = "$USAGI: ni_ifaddrs.c,v 1.8 2007-10-11 06:25:21 yoshfuji Exp $ based on USAGI: ifaddrs.c,v 1.18 2002/03/06 01:50:46 yoshfuji Exp"; + +/* ====================================================================== */ +struct nlmsg_list { + struct nlmsg_list *nlm_next; + struct nlmsghdr *nlh; + int size; + time_t seq; +}; + +#ifndef IFA_LOCAL +struct rtmaddr_ifamap { + void *address; + void *local; + void *broadcast; + int address_len; + int local_len; + int broadcast_len; +}; +#endif + +/* ====================================================================== */ +static int nl_sendreq(int sd, int request, int flags, int *seq) +{ + char reqbuf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + NLMSG_ALIGN(sizeof(struct rtgenmsg))]; + struct sockaddr_nl nladdr; + struct nlmsghdr *req_hdr; + struct rtgenmsg *req_msg; + time_t t = time(NULL); + + if (seq) + *seq = t; + memset(&reqbuf, 0, sizeof(reqbuf)); + req_hdr = (struct nlmsghdr *) reqbuf; + req_msg = (struct rtgenmsg *) NLMSG_DATA(req_hdr); + req_hdr->nlmsg_len = NLMSG_LENGTH(sizeof(*req_msg)); + req_hdr->nlmsg_type = request; + req_hdr->nlmsg_flags = flags | NLM_F_REQUEST; + req_hdr->nlmsg_pid = 0; + req_hdr->nlmsg_seq = t; + req_msg->rtgen_family = AF_UNSPEC; + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + return (sendto(sd, (void *) req_hdr, req_hdr->nlmsg_len, 0, (struct sockaddr *) &nladdr, sizeof(nladdr))); +} + +static int nl_recvmsg(int sd, int request, int seq, void *buf, size_t buflen, int *flags) +{ + struct msghdr msg; + struct iovec iov = { buf, buflen }; + struct sockaddr_nl nladdr; + int read_len; + + for (;;) { + msg.msg_name = (void *) &nladdr; + msg.msg_namelen = sizeof(nladdr); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + read_len = recvmsg(sd, &msg, 0); + if ((read_len < 0 && errno == EINTR) + || (msg.msg_flags & MSG_TRUNC)) + continue; + if (flags) + *flags = msg.msg_flags; + break; + } + return read_len; +} + +static int nl_getmsg(int sd, int request, int seq, struct nlmsghdr **nlhp, int *done) +{ + struct nlmsghdr *nh; + size_t bufsize = 65536, lastbufsize = 0; + void *buff = NULL; + int result = 0, read_size; + int msg_flags; + pid_t pid = getpid(); + for (;;) { + void *newbuff = realloc(buff, bufsize); + if (newbuff == NULL || bufsize < lastbufsize) { + free(newbuff); + result = -1; + break; + } + buff = newbuff; + result = read_size = nl_recvmsg(sd, request, seq, buff, bufsize, &msg_flags); + if (read_size < 0 || (msg_flags & MSG_TRUNC)) { + lastbufsize = bufsize; + bufsize *= 2; + continue; + } + if (read_size == 0) + break; + nh = (struct nlmsghdr *) buff; + for (nh = (struct nlmsghdr *) buff; NLMSG_OK(nh, read_size); nh = (struct nlmsghdr *) NLMSG_NEXT(nh, read_size)) { + if (nh->nlmsg_pid != pid || nh->nlmsg_seq != seq) + continue; + if (nh->nlmsg_type == NLMSG_DONE) { + (*done)++; + break; /* ok */ + } + if (nh->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *nlerr = (struct nlmsgerr *) NLMSG_DATA(nh); + result = -1; + if (nh->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) + errno = EIO; + else + errno = -nlerr->error; + break; + } + } + break; + } + if (result < 0) + if (buff) { + int saved_errno = errno; + free(buff); + buff = NULL; + errno = saved_errno; + } + *nlhp = (struct nlmsghdr *) buff; + return result; +} + +static int nl_getlist(int sd, int seq, int request, struct nlmsg_list **nlm_list, struct nlmsg_list **nlm_end) +{ + struct nlmsghdr *nlh = NULL; + int status; + int done = 0; + + status = nl_sendreq(sd, request, NLM_F_ROOT | NLM_F_MATCH, &seq); + if (status < 0) + return status; + if (seq == 0) + seq = (int) time(NULL); + while (!done) { + status = nl_getmsg(sd, request, seq, &nlh, &done); + if (status < 0) + return status; + if (nlh) { + struct nlmsg_list *nlm_next = (struct nlmsg_list *) malloc(sizeof(struct nlmsg_list)); + if (nlm_next == NULL) { + int saved_errno = errno; + free(nlh); + errno = saved_errno; + status = -1; + } else { + nlm_next->nlm_next = NULL; + nlm_next->nlh = (struct nlmsghdr *) nlh; + nlm_next->size = status; + nlm_next->seq = seq; + if (*nlm_list == NULL) { + *nlm_list = nlm_next; + *nlm_end = nlm_next; + } else { + (*nlm_end)->nlm_next = nlm_next; + *nlm_end = nlm_next; + } + } + } + } + return status >= 0 ? seq : status; +} + +/* ---------------------------------------------------------------------- */ +static void free_nlmsglist(struct nlmsg_list *nlm0) +{ + struct nlmsg_list *nlm, *nlm_next; + int saved_errno; + if (!nlm0) + return; + saved_errno = errno; + nlm = nlm0; + while(nlm) { + if(nlm->nlh) + free(nlm->nlh); + nlm_next = nlm->nlm_next; + free(nlm); + nlm = nlm_next; + } + errno = saved_errno; +} + +static void free_data(void *data) +{ + int saved_errno = errno; + if (data != NULL) + free(data); + errno = saved_errno; +} + +/* ---------------------------------------------------------------------- */ +static void nl_close(int sd) +{ + int saved_errno = errno; + if (sd >= 0) + close(sd); + errno = saved_errno; +} + +/* ---------------------------------------------------------------------- */ +static int nl_open(void) +{ + struct sockaddr_nl nladdr; + int sd; + + sd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sd < 0) + return -1; + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + if (bind(sd, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) { + nl_close(sd); + return -1; + } + return sd; +} + +/* ====================================================================== */ +int ni_ifaddrs(struct ni_ifaddrs **ifap, sa_family_t family) +{ + int sd; + struct nlmsg_list *nlmsg_list, *nlmsg_end, *nlm; + /* - - - - - - - - - - - - - - - */ + int icnt; + size_t dlen, xlen; + uint32_t max_ifindex = 0; + + pid_t pid = getpid(); + int seq = 0; + int build; /* 0 or 1 */ + +/* ---------------------------------- */ + /* initialize */ + icnt = dlen = xlen = 0; + nlmsg_list = nlmsg_end = NULL; + + if (ifap) + *ifap = NULL; + +/* ---------------------------------- */ + /* open socket and bind */ + sd = nl_open(); + if (sd < 0) + return -1; + +/* ---------------------------------- */ + /* gather info */ + if ((seq = nl_getlist(sd, seq + 1, RTM_GETADDR, &nlmsg_list, &nlmsg_end)) < 0) { + free_nlmsglist(nlmsg_list); + nl_close(sd); + return -1; + } + +/* ---------------------------------- */ + /* Estimate size of result buffer and fill it */ + for (build = 0; build <= 1; build++) { + struct ni_ifaddrs *ifl = NULL, *ifa = NULL; + struct nlmsghdr *nlh, *nlh0; + void *data = NULL, *xdata = NULL; +#ifndef IFA_LOCAL + struct rtmaddr_ifamap ifamap; +#endif + + if (build) { + ifa = data = calloc(1, NLMSG_ALIGN(sizeof(struct ni_ifaddrs[icnt])) + + dlen + xlen); + if (ifap != NULL) + *ifap = ifa; + else { + free_data(data); + break; + } + if (data == NULL) { + free_data(data); + break; + } + ifl = NULL; + data += NLMSG_ALIGN(sizeof(struct ni_ifaddrs)) * icnt; + xdata = data + dlen; + } + + for (nlm = nlmsg_list; nlm; nlm = nlm->nlm_next) { + int nlmlen = nlm->size; + if (!(nlh0 = nlm->nlh)) + continue; + for (nlh = nlh0; NLMSG_OK(nlh, nlmlen); nlh = NLMSG_NEXT(nlh, nlmlen)) { + struct ifaddrmsg *ifam = NULL; + struct rtattr *rta; + + size_t nlm_struct_size = 0; + sa_family_t nlm_family = 0; + uint32_t nlm_index = 0; + unsigned int nlm_flags; + size_t rtasize; + +#ifndef IFA_LOCAL + memset(&ifamap, 0, sizeof(ifamap)); +#endif + + /* check if the message is what we want */ + if (nlh->nlmsg_pid != pid || nlh->nlmsg_seq != nlm->seq) + continue; + if (nlh->nlmsg_type == NLMSG_DONE) { + break; /* ok */ + } + switch (nlh->nlmsg_type) { + case RTM_NEWADDR: + ifam = (struct ifaddrmsg *) NLMSG_DATA(nlh); + nlm_struct_size = sizeof(*ifam); + nlm_family = ifam->ifa_family; + nlm_index = ifam->ifa_index; + nlm_flags = ifam->ifa_flags; + if (family && nlm_family != family) + continue; + if (build) { + ifa->ifa_ifindex = nlm_index; + ifa->ifa_flags = nlm_flags; + } + break; + default: + continue; + } + + if (!build) { + if (max_ifindex < nlm_index) + max_ifindex = nlm_index; + } else { + if (ifl != NULL) + ifl->ifa_next = ifa; + } + + rtasize = NLMSG_PAYLOAD(nlh, nlmlen) - NLMSG_ALIGN(nlm_struct_size); + for (rta = (struct rtattr *) (((char *) NLMSG_DATA(nlh)) + + NLMSG_ALIGN(nlm_struct_size)); + RTA_OK(rta, rtasize); + rta = RTA_NEXT(rta, rtasize)) { + void *rtadata = RTA_DATA(rta); + size_t rtapayload = RTA_PAYLOAD(rta); + + switch (nlh->nlmsg_type) { + case RTM_NEWADDR: + if (nlm_family == AF_PACKET) + break; + switch (rta->rta_type) { +#ifndef IFA_LOCAL + case IFA_ADDRESS: + ifamap.address = rtadata; + ifamap.address_len = rtapayload; + break; + case IFA_LOCAL: + ifamap.local = rtadata; + ifamap.local_len = rtapayload; + break; + case IFA_BROADCAST: + ifamap.broadcast = rtadata; + ifamap.broadcast_len = rtapayload; + break; + case IFA_LABEL: + break; + case IFA_UNSPEC: + break; +#else + case IFA_LOCAL: + if (!build) + dlen += NLMSG_ALIGN(rtapayload); + else { + memcpy(data, rtadata, rtapayload); + ifa->ifa_addr = data; + data += NLMSG_ALIGN(rtapayload); + } + break; +#endif + case IFA_CACHEINFO: + if (!build) + xlen += NLMSG_ALIGN(rtapayload); + else { + memcpy(xdata, rtadata, rtapayload); + ifa->ifa_cacheinfo = xdata; + xdata += NLMSG_ALIGN(rtapayload); + } + break; + } + } + } +#ifndef IFA_LOCAL + if (nlh->nlmsg_type == RTM_NEWADDR && nlm_family != AF_PACKET) { + if (!ifamap.local) { + ifamap.local = ifamap.address; + ifamap.local_len = ifamap.address_len; + } + if (!ifamap.address) { + ifamap.address = ifamap.local; + ifamap.address_len = ifamap.local_len; + } + if (ifamap.address_len != ifamap.local_len || + (ifamap.address != NULL && + memcmp(ifamap.address, ifamap.local, ifamap.address_len))) { + /* p2p; address is peer and local is ours */ + ifamap.broadcast = ifamap.address; + ifamap.broadcast_len = ifamap.address_len; + ifamap.address = ifamap.local; + ifamap.address_len = ifamap.local_len; + } + if (ifamap.address) { + if (!build) + dlen += NLMSG_ALIGN(ifamap.address_len); + else { + ifa->ifa_addr = (struct sockaddr *) data; + memcpy(ifa->ifa_addr, ifamap.address, ifamap.address_len); + data += NLMSG_ALIGN(ifamap.address_len); + } + } + } +#endif + if (!build) { + icnt++; + } else { + ifl = ifa++; + } + } + } + if (!build) { + if (icnt == 0 && (dlen + xlen == 0)) { + if (ifap != NULL) + *ifap = NULL; + break; /* cannot found any addresses */ + } + } + } + +/* ---------------------------------- */ + /* Finalize */ + free_nlmsglist(nlmsg_list); + nl_close(sd); + return 0; +} + +/* ---------------------------------------------------------------------- */ +void ni_freeifaddrs(struct ni_ifaddrs *ifa) +{ + free(ifa); +} + diff --git a/ninfod/ni_ifaddrs.h b/ninfod/ni_ifaddrs.h new file mode 100644 index 0000000..e164a8b --- /dev/null +++ b/ninfod/ni_ifaddrs.h @@ -0,0 +1,45 @@ +/* $USAGI: ni_ifaddrs.h,v 1.1 2002-12-03 17:48:53 yoshfuji Exp $ */ +/* + * Copyright (C) 2002 USAGI/WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef NODEINFO_IFADDRS_H +#define NODEINFO_IFADDRS_H + +struct ni_ifaddrs { + struct ni_ifaddrs *ifa_next; + unsigned int ifa_ifindex; + unsigned short ifa_flags; + void *ifa_addr; + struct ifa_cacheinfo *ifa_cacheinfo; +}; + +int ni_ifaddrs(struct ni_ifaddrs **ifap, sa_family_t family); +void ni_freeifaddrs(struct ni_ifaddrs *ifa); + +#endif + diff --git a/ninfod/ninfod.c b/ninfod/ninfod.c new file mode 100644 index 0000000..9bb69c8 --- /dev/null +++ b/ninfod/ninfod.c @@ -0,0 +1,772 @@ +/* $USAGI: ninfod.c,v 1.34 2003-01-15 06:41:23 mk Exp $ */ +/* + * Copyright (C) 2002 USAGI/WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * Author: + * YOSHIFUJI Hideaki + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#if HAVE_SYS_TYPES_H +# include +#endif +#if STDC_HEADERS +# include +# include +# include +# include +#else +# if HAVE_STDLIB_H +# include +# endif +#endif + +#if HAVE_STRING_H +# if !STDC_HEADERS && HAVE_MEMORY_H +# include +# endif +# include +#endif +#if HAVE_STRINGS_H +# include +#endif +#if HAVE_INTTYPES_H +# include +#else +# if HAVE_STDINT_H +# include +# endif +#endif +#if HAVE_LIMITS_H +# include +#endif +#if HAVE_UNISTD_H +# include +#endif + +#ifdef TIME_WITH_SYS_TIME +# include +# include +#else +# ifdef HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif + +#if HAVE_SYS_UIO_H +#include +#endif + +#include + +#if HAVE_NETINET_IN_H +# include +#endif + +#if HAVE_NETINET_ICMP6_H +# include +#endif +#ifndef HAVE_STRUCT_ICMP6_NODEINFO +# include "icmp6_nodeinfo.h" +#endif + +#if HAVE_NETDB_H +# include +#endif +#include + +#include + +#if HAVE_SYSLOG_H +# include +#endif + +#if HAVE_PWD_H +# include +#endif + +#if HAVE_SYS_CAPABILITY_H +# include +# include +#endif + +#include "ninfod.h" + +#ifndef offsetof +# define offsetof(aggregate,member) ((size_t)&((aggregate *)0)->member) +#endif + +/* --------- */ +/* ID */ +static char *RCSID __attribute__ ((unused)) = "$USAGI: ninfod.c,v 1.34 2003-01-15 06:41:23 mk Exp $"; + +/* Variables */ +int sock; +int daemonized; + +char *appname; +static int opt_d = 0; /* debug */ +static int opt_h = 0; /* help */ +static char *opt_p = NINFOD_PIDFILE; /* pidfile */ +static int got_signal = 0; /* loop unless true */ +int opt_v = 0; /* verbose */ +static uid_t opt_u; + +static int ipv6_pktinfo = IPV6_PKTINFO; + +/* --------- */ +#if ENABLE_DEBUG +static const __inline__ char * log_level(int priority) { + switch(priority) { + case LOG_EMERG: return "EMERG"; + case LOG_ALERT: return "ALERT"; + case LOG_CRIT: return "CRIT"; + case LOG_ERR: return "ERR"; + case LOG_WARNING: return "WARNING"; + case LOG_NOTICE: return "NOTICE"; + case LOG_INFO: return "INFO"; + case LOG_DEBUG: return "DEBUG"; + default: return "???"; + } +} + +void stderrlog(int pri, char *fmt, ...) +{ + va_list ap; + char ebuf[512]; + char *buf; + size_t buflen; + + va_start(ap, fmt); + + for (buf = ebuf, buflen = sizeof(ebuf); + buflen < SIZE_MAX / 2; + free(buf != ebuf ? buf : NULL), buf = NULL, buflen *= 2) { + size_t rem; + size_t res; + + buf = malloc(buflen); + if (!buf) + break; /*XXX*/ + + rem = buflen; + + res = snprintf(buf, rem, "[%s] ", log_level(pri)); + if (res >= rem) + continue; + rem -= res; + + res = vsnprintf(buf + res, rem, fmt, ap); + + if (res >= rem) + continue; + break; + } + + if (buf) { + fputs(buf, stderr); + free(buf != ebuf ? buf : NULL); + } + + va_end(ap); +} +#endif + +/* --------- */ +static int __inline__ open_sock(void) +{ + return socket(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6); +} + +static int set_recvpktinfo(int sock) +{ + int on, ret; + + on = 1; + +#if defined(IPV6_RECVPKTINFO) + ret = setsockopt(sock, + IPPROTO_IPV6, IPV6_RECVPKTINFO, + &on, sizeof(on)); + if (!ret) + return 0; +# if defined(IPV6_2292PKTINFO) + ret = setsockopt(sock, + IPPROTO_IPV6, IPV6_2292PKTINFO, + &on, sizeof(on)); + if (!ret) { + ipv6_pktinfo = IPV6_2292PKTINFO; + return 0; + } + + DEBUG(LOG_ERR, "setsockopt(IPV6_RECVPKTINFO/IPV6_2292PKTINFO): %s\n", + strerror(errno)); +# else + DEBUG(LOG_ERR, "setsockopt(IPV6_RECVPKTINFO): %s\n", + strerror(errno)); +# endif +#else + ret = setsockopt(sock, + IPPROTO_IPV6, IPV6_PKTINFO, + &on, sizeof(on)); + if (!ret) + return 0; + + DEBUG(LOG_ERR, "setsockopt(IPV6_PKTINFO): %s\n", + strerror(errno)); +#endif + + return -1; +} + +static int __inline__ init_sock(int sock) +{ + struct icmp6_filter filter; +#if NEED_IPV6CHECKSUM + int i; + + i = offsetof(struct icmp6_nodeinfo, ni_cksum); + if (setsockopt(sock, + IPPROTO_IPV6, IPV6_CHECKSUM, + &i, sizeof(i)) < 0) { + DEBUG(LOG_ERR, "setsockopt(IPV6_CHECKSUM): %s\n", + strerror(errno)); + return -1; + } +#endif + + ICMP6_FILTER_SETBLOCKALL(&filter); + ICMP6_FILTER_SETPASS(ICMP6_NI_QUERY, &filter); + if (setsockopt(sock, + IPPROTO_ICMPV6, ICMP6_FILTER, + &filter, sizeof(filter)) < 0) { + DEBUG(LOG_ERR, "setsockopt(ICMP6_FILTER): %s\n", + strerror(errno)); + return -1; + } + + if (set_recvpktinfo(sock) < 0) + return -1; + + return 0; +} + +/* --------- */ +int ni_recv(struct packetcontext *p) +{ + int sock = p->sock; + struct iovec iov[1]; + struct msghdr msgh; + char recvcbuf[CMSG_SPACE(sizeof(p->pktinfo))]; + struct cmsghdr *cmsg; + int cc; + + DEBUG(LOG_DEBUG, "%s()\n", __func__); + + memset(&iov, 0, sizeof(iov)); + iov[0].iov_base = p->query; + iov[0].iov_len = sizeof(p->query); + + memset(&msgh, 0, sizeof(msgh)); + msgh.msg_name = (struct sockaddr *)&p->addr; + msgh.msg_namelen = sizeof(p->addr); + msgh.msg_iov = iov; + msgh.msg_iovlen = 1; + msgh.msg_control = recvcbuf; + msgh.msg_controllen = sizeof(recvcbuf); + + if ((cc = recvmsg(sock, &msgh, 0)) < 0) + return -1; + + p->querylen = cc; + p->addrlen = msgh.msg_namelen; + + for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg; + cmsg = CMSG_NXTHDR(&msgh, cmsg)) { + if (cmsg->cmsg_level == IPPROTO_IPV6 && + (cmsg->cmsg_type == IPV6_PKTINFO +#if defined(IPV6_2292PKTINFO) + || cmsg->cmsg_type == IPV6_2292PKTINFO +#endif + )) { + memcpy(&p->pktinfo, CMSG_DATA(cmsg), sizeof(p->pktinfo)); + break; + } + } + + return 0; +} + +int ni_send(struct packetcontext *p) +{ + int sock = p->sock; + struct iovec iov[2]; + char cbuf[CMSG_SPACE(sizeof(p->pktinfo))]; + struct msghdr msgh; + struct cmsghdr *cmsg; + int cc; + + DEBUG(LOG_DEBUG, "%s()\n", __func__); + + memset(&iov, 0, sizeof(iov)); + iov[0].iov_base = &p->reply; + iov[0].iov_len = sizeof(p->reply); + iov[1].iov_base = p->replydata; + iov[1].iov_len = p->replydatalen; + + memset(&msgh, 0, sizeof(msgh)); + msgh.msg_name = (struct sockaddr *)&p->addr; + msgh.msg_namelen = p->addrlen; + msgh.msg_iov = iov; + msgh.msg_iovlen = p->replydata ? 2 : 1; + + msgh.msg_control = cbuf; + msgh.msg_controllen = sizeof(cbuf); + + cmsg = CMSG_FIRSTHDR(&msgh); + cmsg->cmsg_level = IPPROTO_IPV6; + cmsg->cmsg_type = ipv6_pktinfo; + cmsg->cmsg_len = CMSG_LEN(sizeof(p->pktinfo)); + memcpy(CMSG_DATA(cmsg), &p->pktinfo, sizeof(p->pktinfo)); + + msgh.msg_controllen = cmsg->cmsg_len; + + if (p->delay) { +#if HAVE_NANOSLEEP + struct timespec ts, rts; + int err = 0; + + rts.tv_sec = p->delay / 1000000; + rts.tv_nsec = (long)(p->delay % 1000000) * 1000; + + do { + ts = rts; + err = nanosleep(&ts, &rts); + } while(err < 0); +#else + usleep(p->delay); /*XXX: signal*/ +#endif + } + + cc = sendmsg(sock, &msgh, 0); + if (cc < 0) + DEBUG(LOG_DEBUG, "sendmsg(): %s\n", strerror(errno)); + + ni_free(p->replydata); + ni_free(p); + + return cc; +} + +/* --------- */ +static void sig_handler(int sig) +{ + if (!got_signal) + DEBUG(LOG_INFO, "singnal(%d) received, quitting.\n", sig); + got_signal = 1; +} + +static void setup_sighandlers(void) +{ + struct sigaction act; + sigset_t smask; + sigemptyset(&smask); + sigaddset(&smask, SIGHUP); + sigaddset(&smask, SIGINT); + sigaddset(&smask, SIGQUIT); + sigaddset(&smask, SIGTERM); + + memset(&act, 0, sizeof(act)); + act.sa_handler = sig_handler; + act.sa_mask = smask; + + sigaction(SIGHUP, &act, NULL); + sigaction(SIGINT, &act, NULL); + sigaction(SIGQUIT, &act, NULL); + sigaction(SIGTERM, &act, NULL); +} + +static void set_logfile(void) +{ + setbuf(stderr, NULL); +#if ENABLE_DEBUG + openlog(NINFOD, 0, LOG_USER); +#endif +} + +static void cleanup_pidfile(void) +{ + if (daemonized && opt_p) { + unlink(opt_p); + DEBUG(LOG_ERR, "failed to unlink file '%s' : %s\n", + opt_p, strerror(errno)); + } +} + +static FILE *fopen_excl(const char *file) +{ +#ifndef __linux__ + int fd; + FILE *fp; + + fd = open(file, O_CREAT | O_RDWR | O_EXCL, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (fd < 0) + return NULL; + + return fdopen(file, "w+"); +#else + return fopen(file, "w+x"); +#endif +} + +static void do_daemonize(void) +{ + FILE *fp = NULL; + pid_t pid; + + if (opt_p) { + if (!access(opt_p, R_OK)) { + if ((fp = fopen(opt_p, "r"))) { + if (fscanf(fp, "%d", &pid) != 1) { + DEBUG(LOG_ERR, "pid file '%s' exists, but read failed.\n", + opt_p); + } else { + DEBUG(LOG_ERR, "pid file '%s' exists : %d\n", + opt_p, pid); + } + fclose(fp); + exit(1); + } + } + + fp = fopen_excl(opt_p); + if (!fp) { + DEBUG(LOG_ERR, "failed to open file '%s': %s\n", + opt_p, strerror(errno)); + exit(1); + } + } + + if (daemon(0, 0) < 0) { + DEBUG(LOG_ERR, "failed to daemon(): %s\n", strerror(errno)); + unlink(opt_p); + exit(1); + } + daemonized = 1; + + if (fp) { + fprintf(fp, "%d\n", getpid()); + fclose(fp); + } +} + +/* --------- */ +#ifdef HAVE_LIBCAP +static const cap_value_t cap_net_raw = CAP_NET_RAW; +static const cap_value_t cap_setuid = CAP_SETUID; +static cap_flag_value_t cap_ok; +#else +static uid_t euid; +#endif + +static void limit_capabilities(void) +{ +#ifdef HAVE_LIBCAP + cap_t cap_p, cap_cur_p; + + cap_p = cap_init(); + if (!cap_p) { + DEBUG(LOG_ERR, "cap_init: %s\n", strerror(errno)); + exit(-1); + } + + cap_cur_p = cap_get_proc(); + if (!cap_cur_p) { + DEBUG(LOG_ERR, "cap_get_proc: %s\n", strerror(errno)); + exit(-1); + } + + /* net_raw + setuid / net_raw */ + cap_get_flag(cap_cur_p, CAP_NET_RAW, CAP_PERMITTED, &cap_ok); + if (cap_ok != CAP_CLEAR) { + cap_set_flag(cap_p, CAP_PERMITTED, 1, &cap_net_raw, CAP_SET); + cap_set_flag(cap_p, CAP_EFFECTIVE, 1, &cap_net_raw, CAP_SET); + } + + cap_get_flag(cap_cur_p, CAP_SETUID, CAP_PERMITTED, &cap_ok); + if (cap_ok != CAP_CLEAR) + cap_set_flag(cap_p, CAP_PERMITTED, 1, &cap_setuid, CAP_SET); + + if (cap_set_proc(cap_p) < 0) { + DEBUG(LOG_ERR, "cap_set_proc: %s\n", strerror(errno)); + if (errno != EPERM) + exit(-1); + } + + if (prctl(PR_SET_KEEPCAPS, 1) < 0) { + DEBUG(LOG_ERR, "prctl: %s\n", strerror(errno)); + exit(-1); + } + + cap_free(cap_cur_p); + cap_free(cap_p); +#else + euid = geteuid(); +#endif +} + +static void drop_capabilities(void) +{ +#ifdef HAVE_LIBCAP + cap_t cap_p; + + cap_p = cap_init(); + if (!cap_p) { + DEBUG(LOG_ERR, "cap_init: %s\n", strerror(errno)); + exit(-1); + } + + /* setuid / setuid */ + if (cap_ok != CAP_CLEAR) { + cap_set_flag(cap_p, CAP_PERMITTED, 1, &cap_setuid, CAP_SET); + cap_set_flag(cap_p, CAP_EFFECTIVE, 1, &cap_setuid, CAP_SET); + + if (cap_set_proc(cap_p) < 0) { + DEBUG(LOG_ERR, "cap_set_proc: %s\n", strerror(errno)); + exit(-1); + } + } + + if (seteuid(opt_u ? opt_u : getuid()) < 0) { + DEBUG(LOG_ERR, "setuid: %s\n", strerror(errno)); + exit(-1); + } + + if (prctl(PR_SET_KEEPCAPS, 0) < 0) { + DEBUG(LOG_ERR, "prctl: %s\n", strerror(errno)); + exit(-1); + } + + cap_clear(cap_p); + if (cap_set_proc(cap_p) < 0) { + DEBUG(LOG_ERR, "cap_set_proc: %s\n", strerror(errno)); + exit(-1); + } + + cap_free(cap_p); +#else + if (setuid(getuid()) < 0) { + DEBUG(LOG_ERR, "setuid: %s\n", strerror(errno)); + exit(-1); + } +#endif +} + +/* --------- */ +static void parse_args(int argc, char **argv) +{ + int c; + unsigned long val; + char *ep; + + /* parse options */ + while ((c = getopt(argc, argv, "dhvp:u:")) != -1) { + switch(c) { + case 'd': /* debug */ + opt_d = 1; + break; + case 'v': /* verbose */ + opt_v = 1; + break; + case 'p': + opt_p = optarg; + break; + case 'u': + val = strtoul(optarg, &ep, 10); + if (!optarg || *ep) { + struct passwd *pw = getpwnam(optarg); + if (!pw) { + DEBUG(LOG_ERR, "No such user: %s", optarg); + exit(1); + } + opt_u = pw->pw_uid; + } else + opt_u = val; + break; + case 'h': /* help */ + default: + opt_h = 1; + break; + } + } + + argc -= optind; +#if 0 + argv += optind; +#endif + + if (argc) + opt_h = 1; +} + +static void print_copying(void) { + fprintf(stderr, + "Node Information Daemon\n" + "Copyright (C)2002 USAGI/WIDE Project. All Rights Reserved.\n" + "\n" + ); +} + +static void print_usage(void) { + fprintf(stderr, + "Usage: %s [-d] [-p pidfile] [-u user] [-h] [-v]\n\n", + appname + ); +} + +/* --------- */ +int main (int argc, char **argv) +{ + int sock_errno = 0; + + appname = argv[0]; + set_logfile(); + + limit_capabilities(); + + sock = open_sock(); + if (sock < 0) + sock_errno = errno; + + parse_args(argc, argv); + + drop_capabilities(); + + if (opt_h || opt_v) + print_copying(); + if (opt_h) { + print_usage(); + exit(1); + } + + if (sock_errno) { + DEBUG(LOG_ERR, "socket: %s\n", strerror(sock_errno)); + exit(1); + } + + /* initialize */ + if (init_sock(sock) < 0) + exit(1); + + setup_sighandlers(); + if (!opt_d) + do_daemonize(); + + init_core(1); + + /* main loop */ + while (!got_signal) { + struct packetcontext *p; + struct icmp6_hdr *icmph; +#if ENABLE_DEBUG + char saddrbuf[NI_MAXHOST]; + int status; +#endif + + init_core(0); + + p = ni_malloc(sizeof(*p)); + if (!p) { + DEBUG(LOG_WARNING, "%s(): failed to allocate packet context; sleep 1 sec.\n", + __func__); + sleep(1); + continue; + } + + while (!got_signal) { + memset(p, 0, sizeof(*p)); + p->sock = sock; + + if (ni_recv(p) < 0) { + if (got_signal) + break; + if (errno == EAGAIN || errno == EINTR) + continue; + /* XXX: syslog */ + continue; + } + break; + } + +#if ENABLE_DEBUG + status = getnameinfo((struct sockaddr *)&p->addr, + p->addrlen, + saddrbuf, sizeof(saddrbuf), + NULL, 0, + NI_NUMERICHOST); + if (status) + sprintf(saddrbuf, "???"); +#endif + init_core(0); + + if (p->querylen < sizeof(struct icmp6_hdr)) { + ni_free(p); + DEBUG(LOG_WARNING, "Too short icmp message from %s\n", saddrbuf); + continue; + } + + icmph = (struct icmp6_hdr *)p->query; + + DEBUG(LOG_DEBUG, + "type=%d, code=%d, cksum=0x%04x\n", + icmph->icmp6_type, icmph->icmp6_code, + ntohs(icmph->icmp6_cksum)); + + if (icmph->icmp6_type != ICMP6_NI_QUERY) { + DEBUG(LOG_WARNING, + "Strange icmp type %d from %s\n", + icmph->icmp6_type, saddrbuf); + ni_free(p); + continue; + } + + pr_nodeinfo(p); /* this frees p */ + } + + cleanup_pidfile(); + + exit(0); +} + diff --git a/ninfod/ninfod.h b/ninfod/ninfod.h new file mode 100644 index 0000000..feda4fa --- /dev/null +++ b/ninfod/ninfod.h @@ -0,0 +1,136 @@ +/* $USAGI: ninfod.h,v 1.20 2002-12-19 15:51:16 yoshfuji Exp $ */ +/* + * Copyright (C) 2002 USAGI/WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * Author: + * YOSHIFUJI Hideaki + */ + +/* definitions */ +#define NINFOD "ninfod" +#define NINFOD_PIDFILE "/var/run/ninfod.pid" + +#define MAX_ANYCAST_DELAY_TIME 1000000.0 /* 1 sec */ + +#define MAX_DNSLABEL_SIZE 63 +#define MAX_DNSNAME_SIZE 255 +#define MAX_QUERY_SIZE (sizeof(struct icmp6_nodeinfo)+MAX_DNSNAME_SIZE+2) +#define MAX_REPLY_SIZE 1280-sizeof(struct ip6_hdr) + +#define MAX_SUPTYPES 32 + +#define CHECKANDFILL_ARGS struct packetcontext *p,\ + char *subject, size_t subjlen, \ + unsigned int flags, \ + unsigned int *subj_if, \ + int reply +#define INIT_ARGS \ + int forced + +struct packetcontext { + /* socket */ + int sock; + + /* query info */ + struct sockaddr_storage addr; + socklen_t addrlen; + struct in6_pktinfo pktinfo; + char query[MAX_QUERY_SIZE]; + int querylen; + + /* reply info */ + struct icmp6_nodeinfo reply; /* common */ + char *replydata; /* data */ + int replydatalen; + + unsigned int delay; /* (random) delay */ +}; + +/* variables */ +extern int opt_v; /* ninfod.c */ +extern int daemonized; /* ninfod.c */ +extern int sock; /* ninfod.c */ +extern int initialized; /* ninfod_core.c */ + +/* ninfod.c* */ +int ni_recv(struct packetcontext *p); +int ni_send(struct packetcontext *p); + +/* ninfod_core.c */ +#if ENABLE_DEBUG +void stderrlog(int priority, char *format, ...); +# define DEBUG(pri, fmt, args...) do { \ + int saved_errno = errno; \ + if (opt_v || pri != LOG_DEBUG) { \ + if (daemonized) { \ + syslog(pri, fmt, ## args); \ + } else { \ + stderrlog(pri, fmt, ## args); \ + } \ + } \ + errno = saved_errno; \ + } while(0) +#else +# define DEBUG(pri, fmt, args...) do { ; } while(0) +#endif + +#define ni_malloc(size) ({ \ + size_t _size = (size); \ + void *p = malloc(_size); \ + DEBUG(LOG_DEBUG, "%s(): malloc(%zu) = %p\n", __func__, _size, p); \ + p; \ + }) +#define ni_free(p) ({ \ + void *_p = (p); \ + int saved_errno = errno; \ + DEBUG(LOG_DEBUG, "%s(): free(%p)\n", __func__, _p); \ + free(_p); \ + errno = saved_errno; \ + }) + +void init_core(int forced); +int pr_nodeinfo(struct packetcontext *p); + +int pr_nodeinfo_unknown(CHECKANDFILL_ARGS); +int pr_nodeinfo_refused(CHECKANDFILL_ARGS); +int pr_nodeinfo_noop(CHECKANDFILL_ARGS); +void init_nodeinfo_suptypes(INIT_ARGS); +int pr_nodeinfo_suptypes(CHECKANDFILL_ARGS); + +/* ninfod_addrs.c */ +void init_nodeinfo_ipv6addr(INIT_ARGS); +int pr_nodeinfo_ipv6addr(CHECKANDFILL_ARGS); +void init_nodeinfo_ipv4addr(INIT_ARGS); +int pr_nodeinfo_ipv4addr(CHECKANDFILL_ARGS); + +/* ninfod_name.c */ +int check_nigroup(const struct in6_addr *addr); +void init_nodeinfo_nodename(INIT_ARGS); +int pr_nodeinfo_nodename(CHECKANDFILL_ARGS); + diff --git a/ninfod/ninfod.sh.in b/ninfod/ninfod.sh.in new file mode 100644 index 0000000..4c5e6b5 --- /dev/null +++ b/ninfod/ninfod.sh.in @@ -0,0 +1,37 @@ +#!/bin/sh + +NINFOD=@prefix@/sbin/ninfod +PID=/var/run/ninfod.pid + +if ! [ -x $NINFOD ]; then + exit 0 +fi + +case "$1" in + start) + echo -n "Starting node infomation daemon:" + echo -n " ninfod" ; + $NINFOD + echo "." + ;; + stop) + echo -n "Stopping node infomation daemon:" + echo -n " ninfod" ; + kill `cat $PID` + echo "." + ;; + restart) + echo -n "Restarting node information daemon:" + echo -n " ninfod" + kill `cat $PID` + $NINFOD + echo "." + ;; + *) + echo "Usage: /etc/init.d/ninfod {start|stop|restart}" + exit 1 + ;; +esac + +exit 0 + diff --git a/ninfod/ninfod_addrs.c b/ninfod/ninfod_addrs.c new file mode 100644 index 0000000..448121f --- /dev/null +++ b/ninfod/ninfod_addrs.c @@ -0,0 +1,469 @@ +/* $USAGI: ninfod_addrs.c,v 1.18 2003-07-16 09:49:01 yoshfuji Exp $ */ +/* + * Copyright (C) 2002 USAGI/WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * Author: + * YOSHIFUJI Hideaki + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#if HAVE_SYS_TYPES_H +# include +#endif + +#if STDC_HEADERS +# include +# include +# include +#else +# if HAVE_STDLIB_H +# include +# endif +#endif +#if HAVE_STRING_H +# if !STDC_HEADERS && HAVE_MEMORY_H +# include +# endif +# include +#endif +#if HAVE_STRINGS_H +# include +#endif +#if HAVE_INTTYPES_H +# include +#else +# if HAVE_STDINT_H +# include +# endif +#endif +#if HAVE_UNISTD_H +# include +#endif + +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif + +#if HAVE_SYS_UIO_H +#include +#endif + +#include +#if HAVE_LINUX_RTNETLINK_H +#include +#include +#endif + +#if HAVE_NETINET_IN_H +# include +#endif + +#if HAVE_NETINET_IP6_H +# include +#endif + +#if HAVE_NETINET_ICMP6_H +# include +#endif +#ifndef HAVE_STRUCT_ICMP6_NODEINFO +# include "icmp6_nodeinfo.h" +#endif + +#if HAVE_NETDB_H +# include +#endif +#include + +#if HAVE_SYSLOG_H +# include +#endif + +#include "ninfod.h" +#include "ni_ifaddrs.h" + +#ifndef offsetof +# define offsetof(aggregate,member) ((size_t)&((aggregate *)0)->member) +#endif + +/* ---------- */ +/* ID */ +static char *RCSID __attribute__ ((unused)) = "$USAGI: ninfod_addrs.c,v 1.18 2003-07-16 09:49:01 yoshfuji Exp $"; + +/* ---------- */ +/* ipv6 address */ +void init_nodeinfo_ipv6addr(INIT_ARGS) +{ + DEBUG(LOG_DEBUG, "%s()\n", __func__); + return; +} + +int filter_ipv6addr(const struct in6_addr *ifaddr, unsigned int flags) +{ + if (IN6_IS_ADDR_UNSPECIFIED(ifaddr) || + IN6_IS_ADDR_LOOPBACK(ifaddr)) { + return 1; + } else if (IN6_IS_ADDR_V4COMPAT(ifaddr) || + IN6_IS_ADDR_V4MAPPED(ifaddr)) { + return !(flags & NI_NODEADDR_FLAG_COMPAT); + } else if (IN6_IS_ADDR_LINKLOCAL(ifaddr)) { + return !(flags & NI_NODEADDR_FLAG_LINKLOCAL); + } else if (IN6_IS_ADDR_SITELOCAL(ifaddr)) { + return !(flags & NI_NODEADDR_FLAG_SITELOCAL); + } + return !(flags & NI_NODEADDR_FLAG_GLOBAL); +} + +int pr_nodeinfo_ipv6addr(CHECKANDFILL_ARGS) +{ + struct ni_ifaddrs *ifa0; + unsigned int ifindex = 0; + + DEBUG(LOG_DEBUG, "%s()\n", __func__); + + if (subject && subjlen != sizeof(struct in6_addr)) { + DEBUG(LOG_INFO, + "%s(): invalid subject length %zu for IPv6 Address Subject\n", + __func__, subjlen); + return 1; + } + if (ni_ifaddrs(&ifa0, AF_INET6)) + return -1; /* failed to get addresses */ + + /* pass 0: consider subject and determine subjected interface */ + if (subject) { + struct ni_ifaddrs *ifa; + + for (ifa = ifa0; ifa; ifa = ifa->ifa_next) { + if (!ifa->ifa_addr) + continue; + if (ifa->ifa_flags & (IFA_F_TENTATIVE|IFA_F_SECONDARY)) + continue; + if (!ifindex && + IN6_ARE_ADDR_EQUAL(&p->pktinfo.ipi6_addr, + (struct in6_addr *)subject)) { + /* + * if subject is equal to destination + * address, receiving interface is + * the candidate subject interface. + */ + ifindex = p->pktinfo.ipi6_ifindex; + } + if (!IN6_IS_ADDR_LOOPBACK((struct in6_addr *)subject) && + IN6_ARE_ADDR_EQUAL((struct in6_addr *)ifa->ifa_addr, + (struct in6_addr *)subject)) { + /* + * address is assigned on some interface. + * if multiple interfaces have the same interface, + * 1) prefer receiving interface + * 2) use first found one + */ + if (!ifindex || + (p->pktinfo.ipi6_ifindex == ifindex)) + ifindex = ifa->ifa_ifindex; + } + } + if (!ifindex) { + ni_freeifaddrs(ifa0); + return 1; /* subject not found */ + } + if (subj_if) + *subj_if = ifindex; + } else { + ifindex = subj_if ? *subj_if : 0; + if (ifindex == 0) + ifindex = p->pktinfo.ipi6_ifindex; + if (ifindex == 0) { + ni_freeifaddrs(ifa0); + return 1; /* XXX */ + } + } + + if (reply) { + struct ni_ifaddrs *ifa; + unsigned int addrs0 = 0, paddrs0 = 0; + unsigned int addrs, paddrs = 0, daddrs = 0; + + flags &= ~NI_NODEADDR_FLAG_TRUNCATE; + + /* pass 1: count addresses and preferred addresses to be returned */ + for (ifa = ifa0; ifa; ifa = ifa->ifa_next) { + if (!ifa->ifa_addr) + continue; + if (ifa->ifa_flags & (IFA_F_TENTATIVE|IFA_F_SECONDARY)) + continue; + if (!(flags & NI_NODEADDR_FLAG_ALL) && + ifa->ifa_ifindex != ifindex) + continue; + if (filter_ipv6addr((struct in6_addr *)ifa->ifa_addr, flags)) + continue; + + if (addrs0 + 1 >= ((MAX_REPLY_SIZE - sizeof(struct icmp6_nodeinfo)) / (sizeof(uint32_t) + sizeof(struct in6_addr)))) { + flags |= ~NI_NODEADDR_FLAG_TRUNCATE; + break; + } + + addrs0++; + if (!(ifa->ifa_flags & IFA_F_DEPRECATED)) + paddrs0++; + } + + p->reply.ni_type = ICMP6_NI_REPLY; + p->reply.ni_code = ICMP6_NI_SUCCESS; + p->reply.ni_cksum = 0; + p->reply.ni_qtype = htons(NI_QTYPE_NODEADDR); + p->reply.ni_flags = flags&(NI_NODEADDR_FLAG_COMPAT| + NI_NODEADDR_FLAG_LINKLOCAL| + NI_NODEADDR_FLAG_SITELOCAL| + NI_NODEADDR_FLAG_GLOBAL); + + /* pass 2: store addresses */ + p->replydatalen = (sizeof(uint32_t)+sizeof(struct in6_addr)) * addrs0; + p->replydata = p->replydatalen ? ni_malloc(p->replydatalen) : NULL; + + if (p->replydatalen && !p->replydata) { + p->reply.ni_flags |= NI_NODEADDR_FLAG_TRUNCATE; + addrs0 = paddrs0 = 0; + } + + for (ifa = ifa0, addrs = 0; + ifa && addrs < addrs0; + ifa = ifa->ifa_next) { + char *cp; + uint32_t ttl; + + if (!ifa->ifa_addr) + continue; + if (ifa->ifa_flags & (IFA_F_TENTATIVE|IFA_F_SECONDARY)) + continue; + if (!(flags & NI_NODEADDR_FLAG_ALL) && + ((subj_if && *subj_if) ? (ifa->ifa_ifindex != *subj_if) : + (ifa->ifa_ifindex != p->pktinfo.ipi6_ifindex))) + continue; + if (filter_ipv6addr((struct in6_addr *)ifa->ifa_addr, flags)) + continue; + +#if ENABLE_TTL + if (ifa->ifa_cacheinfo) { + ttl = ifa->ifa_cacheinfo->ifa_valid > 0x7fffffff ? + htonl(0x7fffffff) : htonl(ifa->ifa_cacheinfo->ifa_valid); + } else { + ttl = (ifa->ifa_flags & IFA_F_PERMANENT) ? htonl(0x7fffffff) : 0; + } +#else + ttl = 0; +#endif + + cp = p->replydata + + (sizeof(uint32_t)+sizeof(struct in6_addr)) * (ifa->ifa_flags & IFA_F_DEPRECATED ? paddrs0+daddrs : paddrs); + memcpy(cp, &ttl, sizeof(ttl)); + memcpy(cp + sizeof(ttl), ifa->ifa_addr, sizeof(struct in6_addr)); + + addrs++; + if (ifa->ifa_flags & IFA_F_DEPRECATED) + daddrs++; + else + paddrs++; + } + } + + ni_freeifaddrs(ifa0); + return 0; +} + +/* ipv4 address */ +void init_nodeinfo_ipv4addr(INIT_ARGS) +{ + DEBUG(LOG_DEBUG, "%s()\n", __func__); + return; +} + +int filter_ipv4addr(const struct in_addr *ifaddr, unsigned int flags) +{ + return 0; +} + +int pr_nodeinfo_ipv4addr(CHECKANDFILL_ARGS) +{ + struct ni_ifaddrs *ifa0; + unsigned int ifindex = 0; + + DEBUG(LOG_DEBUG, "%s()\n", __func__); + + if (subject && subjlen != sizeof(struct in_addr)) { + DEBUG(LOG_INFO, + "%s(): invalid subject length %zu for IPv4 Address Subject\n", + __func__, subjlen); + return 1; + } + if (ni_ifaddrs(&ifa0, AF_INET)) + return -1; /* failed to get addresses */ + + /* pass 0: consider subject and determine subjected interface */ + if (subject) { + struct ni_ifaddrs *ifa; + + for (ifa = ifa0; ifa; ifa = ifa->ifa_next) { + if (!ifa->ifa_addr) + continue; + if (ifa->ifa_flags & (IFA_F_TENTATIVE|IFA_F_SECONDARY)) + continue; + if ((((struct in_addr *)subject)->s_addr != htonl(INADDR_LOOPBACK)) && + memcmp((struct in_addr *)ifa->ifa_addr, + (struct in_addr *)subject, + sizeof(struct in_addr)) == 0) { + /* + * address is assigned on some interface. + * if multiple interfaces have the same interface, + * 1) prefer receiving interface + * 2) use first found one + */ + if (!ifindex || + (p->pktinfo.ipi6_ifindex == ifindex)) + ifindex = ifa->ifa_ifindex; + } + } + if (!ifindex) { + ni_freeifaddrs(ifa0); + return 1; /* subject not found */ + } + if (subj_if) + *subj_if = ifindex; + } else { + ifindex = subj_if ? *subj_if : 0; + if (ifindex == 0) + ifindex = p->pktinfo.ipi6_ifindex; + if (ifindex == 0) { + ni_freeifaddrs(ifa0); + return 1; /* XXX */ + } + } + + if (reply) { + struct ni_ifaddrs *ifa; + unsigned int addrs0 = 0, paddrs0 = 0; + unsigned int addrs, paddrs = 0, daddrs = 0; + + flags &= ~NI_IPV4ADDR_FLAG_TRUNCATE; + + /* pass 1: count addresses and preferred addresses to be returned */ + for (ifa = ifa0; ifa; ifa = ifa->ifa_next) { + if (!ifa->ifa_addr) + continue; +#if 1 /* not used in kernel */ + if (ifa->ifa_flags & (IFA_F_TENTATIVE)) + continue; +#endif + if (!(flags & NI_NODEADDR_FLAG_ALL) && + ((subj_if && *subj_if) ? (ifa->ifa_ifindex != *subj_if) : + (ifa->ifa_ifindex != p->pktinfo.ipi6_ifindex))) + continue; + if (filter_ipv4addr((struct in_addr *)ifa->ifa_addr, flags)) + continue; + + if (addrs0 + 1 >= ((MAX_REPLY_SIZE - sizeof(struct icmp6_nodeinfo)) / (sizeof(uint32_t) + sizeof(struct in_addr)))) { + flags |= NI_IPV4ADDR_FLAG_TRUNCATE; + break; + } + + addrs0++; + if (!(ifa->ifa_flags & IFA_F_DEPRECATED)) + paddrs0++; + } + + p->reply.ni_type = ICMP6_NI_REPLY; + p->reply.ni_code = ICMP6_NI_SUCCESS; + p->reply.ni_cksum = 0; + p->reply.ni_qtype = htons(NI_QTYPE_IPV4ADDR); + p->reply.ni_flags = flags & NI_IPV4ADDR_FLAG_ALL; + + /* pass 2: store addresses */ + p->replydatalen = (sizeof(uint32_t)+sizeof(struct in_addr)) * addrs0; + p->replydata = addrs0 ? ni_malloc(p->replydatalen) : NULL; + + if (p->replydatalen && !p->replydata) { + p->reply.ni_flags |= NI_NODEADDR_FLAG_TRUNCATE; + addrs0 = paddrs0 = 0; + } + + for (ifa = ifa0, addrs = 0; + ifa && addrs < addrs0; + ifa = ifa->ifa_next) { + char *cp; + uint32_t ttl; + + if (!ifa->ifa_addr) + continue; +#if 1 /* not used in kernel */ + if (ifa->ifa_flags & (IFA_F_TENTATIVE)) + continue; +#endif + if (!(flags & NI_NODEADDR_FLAG_ALL) && + (ifa->ifa_ifindex != ifindex)) + continue; + if (filter_ipv4addr((struct in_addr *)ifa->ifa_addr, flags)) + continue; + +#if ENABLE_TTL + if (ifa->ifa_cacheinfo) { + ttl = ifa->ifa_cacheinfo->ifa_valid > 0x7fffffff ? + htonl(0x7fffffff) : htonl(ifa->ifa_cacheinfo->ifa_valid); + } else { + ttl = 0; /*XXX*/ + } +#else + ttl = 0; +#endif + + cp = (p->replydata + + (sizeof(uint32_t)+sizeof(struct in_addr)) * (ifa->ifa_flags & IFA_F_DEPRECATED ? paddrs0+daddrs : paddrs)); + memcpy(cp, &ttl, sizeof(ttl)); + memcpy(cp + sizeof(ttl), ifa->ifa_addr, sizeof(struct in_addr)); + + addrs++; + if (ifa->ifa_flags & IFA_F_DEPRECATED) + daddrs++; + else + paddrs++; + } + } + + ni_freeifaddrs(ifa0); + return 0; +} + diff --git a/ninfod/ninfod_core.c b/ninfod/ninfod_core.c new file mode 100644 index 0000000..389f5c0 --- /dev/null +++ b/ninfod/ninfod_core.c @@ -0,0 +1,698 @@ +/* $USAGI: ninfod_core.c,v 1.29 2003-07-16 09:49:01 yoshfuji Exp $ */ +/* + * Copyright (C) 2002 USAGI/WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * Author: + * YOSHIFUJI Hideaki + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#if HAVE_SYS_TYPES_H +# include +#endif +#if STDC_HEADERS +# include +# include +# include +#else +# if HAVE_STDLIB_H +# include +# endif +#endif +#if ENABLE_THREADS && HAVE_PTHREAD_H +# include +#endif +#if HAVE_STRING_H +# if !STDC_HEADERS && HAVE_MEMORY_H +# include +# endif +# include +#endif +#if HAVE_STRINGS_H +# include +#endif +#if HAVE_INTTYPES_H +# include +#else +# if HAVE_STDINT_H +# include +# endif +#endif +#if HAVE_UNISTD_H +# include +#endif + +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif + +#if HAVE_SYS_UIO_H +#include +#endif + +#if HAVE_NETINET_IN_H +# include +#endif + +#if HAVE_NETINET_ICMP6_H +# include +#endif +#ifndef HAVE_STRUCT_ICMP6_NODEINFO +# include "icmp6_nodeinfo.h" +#endif + +#if HAVE_NETDB_H +# include +#endif +#include + +#if HAVE_SYSLOG_H +# include +#endif + +#include "ninfod.h" + +#ifndef offsetof +# define offsetof(aggregate,member) ((size_t)&((aggregate *)0)->member) +#endif + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) + +/* ---------- */ +/* ID */ +static char *RCSID __attribute__ ((unused)) = "$USAGI: ninfod_core.c,v 1.29 2003-07-16 09:49:01 yoshfuji Exp $"; + +/* Variables */ +int initialized = 0; + +#if ENABLE_THREADS && HAVE_LIBPTHREAD +pthread_attr_t pattr; +#endif + +static uint32_t suptypes[(MAX_SUPTYPES+31)>>5]; +static size_t suptypes_len; + +/* ---------- */ +struct subjinfo { + uint8_t code; + char *name; + int (*checksubj)(CHECKANDFILL_ARGS); + int (*init)(INIT_ARGS); +}; + +static struct subjinfo subjinfo_table [] = { + [ICMP6_NI_SUBJ_IPV6] = { + .code = ICMP6_NI_SUBJ_IPV6, + .name = "IPv6", + //.init = init_nodeinfo_ipv6addr, + .checksubj = pr_nodeinfo_ipv6addr, + }, + [ICMP6_NI_SUBJ_FQDN] = { + .code = ICMP6_NI_SUBJ_FQDN, + .name = "FQDN", + //.init = init_nodeinfo_nodename, + .checksubj = pr_nodeinfo_nodename, + }, + [ICMP6_NI_SUBJ_IPV4] = { + .code = ICMP6_NI_SUBJ_IPV4, + .name = "IPv4", + //.init = init_nodeinfo_ipv4addr, + .checksubj = pr_nodeinfo_ipv4addr, + }, +}; + +static struct subjinfo subjinfo_null = { + .name = "null", + .checksubj = pr_nodeinfo_noop, +}; + +static __inline__ struct subjinfo *subjinfo_lookup(int code) +{ + if (code >= ARRAY_SIZE(subjinfo_table)) + return NULL; + if (subjinfo_table[code].name == NULL) + return NULL; + return &subjinfo_table[code]; +} + +/* ---------- */ +#define QTYPEINFO_F_RATELIMIT 0x1 + +struct qtypeinfo { + uint16_t qtype; + char *name; + int (*getreply)(CHECKANDFILL_ARGS); + void (*init)(INIT_ARGS); + int flags; +}; + +static struct qtypeinfo qtypeinfo_table[] = { + [NI_QTYPE_NOOP] = { + .qtype = NI_QTYPE_NOOP, + .name = "NOOP", + .getreply = pr_nodeinfo_noop, + }, +#if ENABLE_SUPTYPES + [NI_QTYPE_SUPTYPES] = { + .qtype = NI_QTYPE_SUPTYPES, + .name = "SupTypes", + .getreply = pr_nodeinfo_suptypes, + .init = init_nodeinfo_suptypes, + }, +#endif + [NI_QTYPE_DNSNAME] = { + .qtype = NI_QTYPE_DNSNAME, + .name = "DnsName", + .getreply = pr_nodeinfo_nodename, + .init = init_nodeinfo_nodename, + }, + [NI_QTYPE_NODEADDR] = { + .qtype = NI_QTYPE_NODEADDR, + .name = "NodeAddr", + .getreply = pr_nodeinfo_ipv6addr, + .init = init_nodeinfo_ipv6addr, + }, + [NI_QTYPE_IPV4ADDR] = { + .qtype = NI_QTYPE_IPV4ADDR, + .name = "IPv4Addr", + .getreply = pr_nodeinfo_ipv4addr, + .init = init_nodeinfo_ipv4addr, + }, +}; + +static struct qtypeinfo qtypeinfo_unknown = { + .name = "unknown", + .getreply = pr_nodeinfo_unknown, + .flags = QTYPEINFO_F_RATELIMIT, +}; + +static struct qtypeinfo qtypeinfo_refused = { + .name = "refused", + .getreply = pr_nodeinfo_refused, + .flags = QTYPEINFO_F_RATELIMIT, +}; + +static __inline__ struct qtypeinfo *qtypeinfo_lookup(int qtype) +{ + if (qtype >= ARRAY_SIZE(qtypeinfo_table)) + return &qtypeinfo_unknown; + if (qtypeinfo_table[qtype].name == NULL) + return &qtypeinfo_unknown; + return &qtypeinfo_table[qtype]; +} + +/* ---------- */ +/* noop */ +int pr_nodeinfo_noop(CHECKANDFILL_ARGS) +{ + DEBUG(LOG_DEBUG, "%s()\n", __func__); + + if (subjlen) { + DEBUG(LOG_WARNING, + "%s(): invalid subject length(%zu)\n", + __func__, subjlen); + return 1; + } + + if (reply) { + p->reply.ni_type = ICMP6_NI_REPLY; + p->reply.ni_code = ICMP6_NI_SUCCESS; + p->reply.ni_cksum = 0; + p->reply.ni_qtype = htons(NI_QTYPE_NOOP); + p->reply.ni_flags = flags; + } + + if (subj_if) + *subj_if = 0; + + return 0; +} + +#if ENABLE_SUPTYPES +/* suptypes */ +int pr_nodeinfo_suptypes(CHECKANDFILL_ARGS) +{ + DEBUG(LOG_DEBUG, "%s()\n", __func__); + + if (subjlen) { + DEBUG(LOG_WARNING, "%s(): invalid subject length(%zu)\n", + __func__, subjlen); + return 1; + } + + if (reply) { + p->reply.ni_type = ICMP6_NI_REPLY; + p->reply.ni_code = ICMP6_NI_SUCCESS; + p->reply.ni_cksum = 0; + p->reply.ni_qtype = htons(NI_QTYPE_SUPTYPES); + p->reply.ni_flags = flags&~NI_SUPTYPE_FLAG_COMPRESS; + + p->replydatalen = suptypes_len<<2; + p->replydata = ni_malloc(p->replydatalen); + if (p->replydata == NULL) { + p->replydatalen = -1; + return -1; /*XXX*/ + } + + memcpy(p->replydata, suptypes, p->replydatalen); + } + return 0; +} + +void init_nodeinfo_suptypes(INIT_ARGS) +{ + size_t w, b; + int i; + + if (!forced && initialized) + return; + + memset(suptypes, 0, sizeof(suptypes)); + suptypes_len = 0; + + for (i=0; i < ARRAY_SIZE(qtypeinfo_table); i++) { + unsigned short qtype; + + if (qtypeinfo_table[i].name == NULL) + continue; + qtype = qtypeinfo_table[i].qtype; + w = qtype>>5; + b = qtype&0x1f; + if (w >= ARRAY_SIZE(suptypes)) { + /* This is programming error. */ + DEBUG(LOG_ERR, "Warning: Too Large Supported Types\n"); + exit(1); + } + suptypes[w] |= htonl(1<reply.ni_type = ICMP6_NI_REPLY; + p->reply.ni_code = ICMP6_NI_UNKNOWN; + p->reply.ni_cksum = 0; + //p->reply.ni_qtype = 0; + p->reply.ni_flags = flags; + + p->replydata = NULL; + p->replydatalen = 0; + + return 0; +} + +/* refused response */ +int pr_nodeinfo_refused(CHECKANDFILL_ARGS) +{ + if (!reply) + return -1; /*???*/ + + p->reply.ni_type = ICMP6_NI_REPLY; + p->reply.ni_code = ICMP6_NI_REFUSED; + p->reply.ni_cksum = 0; + //p->reply.ni_qtype = 0; + p->reply.ni_flags = flags; + + p->replydata = NULL; + p->replydatalen = 0; + + return 0; +} + +/* ---------- */ +/* Policy */ +static int ni_policy(struct packetcontext *p) +{ + const struct in6_addr *saddr = &((const struct sockaddr_in6 *)&p->addr)->sin6_addr; + + /* + * >0: reply + * 0: refused + * <0: discard + */ + + /* Default policy is to refuse queries from + * non-local addresses; loopback, link-local or + * site-local are okay + */ + if (!(IN6_IS_ADDR_LINKLOCAL(saddr) || + IN6_IS_ADDR_SITELOCAL(saddr) || + IN6_IS_ADDR_LOOPBACK(saddr))) + return 0; + return 1; +} + +/* ---------- */ +void init_core(int forced) +{ + int i; + + DEBUG(LOG_DEBUG, "%s()\n", __func__); + + if (!initialized || forced) { + struct timeval tv; + unsigned int seed = 0; + pid_t pid; + + if (gettimeofday(&tv, NULL) < 0) { + DEBUG(LOG_WARNING, "%s(): failed to gettimeofday()\n", __func__); + } else { + seed = (tv.tv_usec & 0xffffffff); + } + + pid = getpid(); + seed ^= (((unsigned long)pid) & 0xffffffff); + + srand(seed); + +#if ENABLE_THREADS && HAVE_LIBPTHREAD + if (initialized) + pthread_attr_destroy(&pattr); + + pthread_attr_init(&pattr); + pthread_attr_setdetachstate(&pattr, PTHREAD_CREATE_DETACHED); +#endif + } + + for (i=0; i < ARRAY_SIZE(subjinfo_table); i++) { + if (subjinfo_table[i].name == NULL) + continue; + if (subjinfo_table[i].init) + subjinfo_table[i].init(forced); + } + + for (i=0; i < ARRAY_SIZE(qtypeinfo_table); i++) { + if (qtypeinfo_table[i].name == NULL) + continue; + if (qtypeinfo_table[i].init) + qtypeinfo_table[i].init(forced); + } + + initialized = 1; + + return; +} + +#if ENABLE_THREADS && HAVE_LIBPTHREAD +static void *ni_send_thread(void *data) +{ + int ret; + DEBUG(LOG_DEBUG, "%s(): thread=%ld\n", __func__, pthread_self()); + ret = ni_send(data); + DEBUG(LOG_DEBUG, "%s(): thread=%ld => %d\n", __func__, pthread_self(), ret); + return NULL; +} +#else +static int ni_send_fork(struct packetcontext *p) +{ + pid_t child = fork(); + if (child < 0) + return -1; + if (child == 0) { + pid_t grandchild = fork(); + if (grandchild < 0) + exit(1); + if (grandchild == 0) { + int ret; + DEBUG(LOG_DEBUG, "%s(): worker=%d\n", + __func__, getpid()); + ret = ni_send(p); + DEBUG(LOG_DEBUG, "%s(): worker=%d => %d\n", + __func__, getpid(), ret); + exit(ret > 0 ? 1 : 0); + } + ni_free(p->replydata); + ni_free(p); + exit(0); + } else { + waitpid(child, NULL, 0); + ni_free(p->replydata); + ni_free(p); + } + return 0; +} +#endif + +static int ni_ratelimit(void) +{ + static struct timeval last; + struct timeval tv, sub; + + if (gettimeofday(&tv, NULL) < 0) { + DEBUG(LOG_WARNING, "%s(): gettimeofday(): %s\n", + __func__, strerror(errno)); + return -1; + } + + if (!timerisset(&last)) { + last = tv; + return 0; + } + + timersub(&tv, &last, &sub); + + if (sub.tv_sec < 1) + return 1; + + last = tv; + return 0; +} + +int pr_nodeinfo(struct packetcontext *p) +{ + struct icmp6_nodeinfo *query = (struct icmp6_nodeinfo *)p->query; + + char *subject = (char *)(query + 1); + size_t subjlen; + struct subjinfo *subjinfo; + struct qtypeinfo *qtypeinfo; + int replyonsubjcheck = 0; + unsigned int subj_if; +#if ENABLE_DEBUG + char printbuf[128]; + int i; + char *cp; +#endif +#if ENABLE_THREADS && HAVE_PTHREAD_H + pthread_t thread; +#endif + int rc; + + /* Step 0: Check destination address + * discard non-linklocal multicast + * discard non-nigroup multicast address(?) + */ + if (IN6_IS_ADDR_MULTICAST(&p->pktinfo.ipi6_addr)) { + if (!IN6_IS_ADDR_MC_LINKLOCAL(&p->pktinfo.ipi6_addr)) { + DEBUG(LOG_WARNING, + "Destination is non-link-local multicast address.\n"); + ni_free(p); + return -1; + } +#if 0 + /* Do not discard NI Queries to multicast address + * other than its own NI Group Address(es) by default. + */ + if (!check_nigroup(&p->pktinfo.ipi6_addr)) { + DEBUG(LOG_WARNING, + "Destination is link-local multicast address other than " + "NI Group address.\n"); + ni_free(p); + return -1; + } +#endif + } + + /* Step 1: Check length */ + if (p->querylen < sizeof(struct icmp6_nodeinfo)) { + DEBUG(LOG_WARNING, "Query too short\n"); + ni_free(p); + return -1; + } + +#if ENABLE_DEBUG + cp = printbuf; + for (i = 0; i < sizeof(query->icmp6_ni_nonce); i++) { + cp += sprintf(cp, " %02x", query->icmp6_ni_nonce[i]); + } + DEBUG(LOG_DEBUG, "%s(): qtype=%d, flags=0x%04x, nonce[] = {%s }\n", + __func__, + ntohs(query->ni_qtype), ntohs(query->ni_flags), printbuf); +#endif + + subjlen = p->querylen - sizeof(struct icmp6_nodeinfo); + + /* Step 2: Check Subject Code */ + switch(htons(query->ni_qtype)) { + case NI_QTYPE_NOOP: + case NI_QTYPE_SUPTYPES: + if (query->ni_code != ICMP6_NI_SUBJ_FQDN) { + DEBUG(LOG_WARNING, + "%s(): invalid/unknown code %u\n", + __func__, query->ni_code); + subjlen = 0; + } + subjinfo = &subjinfo_null; + break; + default: + subjinfo = subjinfo_lookup(query->ni_code); + if (!subjinfo) { + DEBUG(LOG_WARNING, + "%s(): unknown code %u\n", + __func__, query->ni_code); + ni_free(p); + return -1; + } + } + + /* Step 3: Lookup Qtype */ + qtypeinfo = qtypeinfo_lookup(ntohs(query->ni_qtype)); + + /* Step 4: Check Subject + * (And fill reply if it is available now) + */ + if (qtypeinfo->getreply == subjinfo->checksubj) + replyonsubjcheck = 1; + + if (subjinfo->checksubj(p, + subject, subjlen, + query->ni_flags, + replyonsubjcheck ? NULL : &subj_if, + replyonsubjcheck)) { + if (p->replydatalen < 0) { + DEBUG(LOG_WARNING, + "failed to make reply: %s\n", + strerror(errno)); + } + ni_free(p); + return -1; + } + + /* XXX: Step 5: Check the policy */ + rc = ni_policy(p); + if (rc <= 0) { + ni_free(p->replydata); + p->replydata = NULL; + p->replydatalen = 0; + if (rc < 0) { + DEBUG(LOG_WARNING, "Ignored by policy.\n"); + ni_free(p); + return -1; + } + DEBUG(LOG_WARNING, "Refused by policy.\n"); + replyonsubjcheck = 0; + qtypeinfo = &qtypeinfo_refused; + } + + /* Step 6: Fill the reply if not yet done */ + if (!replyonsubjcheck) { + if (qtypeinfo->getreply(p, + NULL, 0, + query->ni_flags, + &subj_if, + 1)) { + if (p->replydatalen) { + DEBUG(LOG_WARNING, + "failed to make reply: %s\n", + strerror(errno)); + } + ni_free(p); + return -1; + } + } + + /* Step 7: Rate Limit */ + if (qtypeinfo->flags&QTYPEINFO_F_RATELIMIT && + ni_ratelimit()) { + ni_free(p->replydata); + ni_free(p); + return -1; + } + + /* Step 8: Fill Qtype / Nonce */ + p->reply.ni_qtype = query->ni_qtype; + memcpy(p->reply.icmp6_ni_nonce, query->icmp6_ni_nonce, sizeof(p->reply.icmp6_ni_nonce)); + + /* Step 9: Source address selection */ + if (IN6_IS_ADDR_MULTICAST(&p->pktinfo.ipi6_addr)) { + /* if query was sent to multicast address, + * use source address selection in kernel. + * XXX: anycast? + */ + memset(&p->pktinfo.ipi6_addr, 0, sizeof(p->pktinfo.ipi6_addr)); + + /* Random Delay between zero and MAX_ANYCAST_DELAY_TIME is + * required if query was sent to anycast or multicast address. + */ + p->delay = (int) (MAX_ANYCAST_DELAY_TIME*rand()/(RAND_MAX+1.0)); + } else { + p->delay = 0; + } + + /* Step 10: Send the reply + * XXX: with possible random delay */ +#if ENABLE_THREADS && HAVE_LIBPTHREAD + /* ni_send_thread() frees p */ + if (pthread_create(&thread, &pattr, ni_send_thread, p)) { + ni_free(p->replydata); + ni_free(p); + return -1; + } +#else + /* ni_send_fork() frees p */ + if (ni_send_fork(p)) { + ni_free(p->replydata); + ni_free(p); + return -1; + } +#endif + + return 0; +} + diff --git a/ninfod/ninfod_name.c b/ninfod/ninfod_name.c new file mode 100644 index 0000000..78b0f27 --- /dev/null +++ b/ninfod/ninfod_name.c @@ -0,0 +1,392 @@ +/* $USAGI: ninfod_name.c,v 1.15 2003-01-11 14:33:28 yoshfuji Exp $ */ +/* + * Copyright (C) 2002 USAGI/WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * Author: + * YOSHIFUJI Hideaki + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#if HAVE_SYS_TYPES_H +# include +#endif +#if STDC_HEADERS +# include +# include +# include +# include +#else +# if HAVE_STDLIB_H +# include +# endif +#endif +#if HAVE_STRING_H +# if !STDC_HEADERS && HAVE_MEMORY_H +# include +# endif +# include +#endif +#if HAVE_STRINGS_H +# include +#endif +#if HAVE_INTTYPES_H +# include +#else +# if HAVE_STDINT_H +# include +# endif +#endif +#if HAVE_UNISTD_H +# include +#endif + +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif + +#if HAVE_SYS_UIO_H +#include +#endif + +#include + +#if HAVE_NETINET_IN_H +# include +#endif + +#if HAVE_NETINET_ICMP6_H +# include +#endif +#ifndef HAVE_STRUCT_ICMP6_NODEINFO +# include "icmp6_nodeinfo.h" +#endif + +#include + +#if defined(HAVE_GCRYPT_H) +# define USE_GCRYPT +# include "iputils_md5dig.h" +#elif defined(HAVE_GNUTLS_OPENSSL_H) +# include +#elif defined(HAVE_OPENSSL_MD5_H) +# include +#endif + +#if HAVE_SYS_UTSNAME_H +# include +#endif +#if HAVE_NETDB_H +# include +#endif +#include + +#if HAVE_SYSLOG_H +# include +#endif + +#include "ninfod.h" + +#ifndef offsetof +# define offsetof(aggregate,member) ((size_t)&((aggregate *)0)->member) +#endif + +/* Hmm,,, */ +#ifndef IPV6_JOIN_GROUP +# define IPV6_JOIN_GROUP IPV6_ADD_MEMBERSHIP +# define IPV6_LEAVE_GROUP IPV6_DROP_MEMBERSHIP +#endif + +/* ---------- */ +/* ID */ +static char *RCSID __attribute__ ((unused)) = "$USAGI: ninfod_name.c,v 1.15 2003-01-11 14:33:28 yoshfuji Exp $"; + +/* Variables */ +static struct utsname utsname; +static char *uts_nodename = utsname.nodename; + +char nodename[MAX_DNSNAME_SIZE]; +static size_t nodenamelen; + +static struct ipv6_mreq nigroup; + +/* ---------- */ +/* Functions */ +int check_nigroup(const struct in6_addr *addr) +{ + return IN6_IS_ADDR_MULTICAST(&nigroup.ipv6mr_multiaddr) && + IN6_ARE_ADDR_EQUAL(&nigroup.ipv6mr_multiaddr, addr); +} + +static int encode_dnsname(const char *name, + char *buf, size_t buflen, + int fqdn) +{ + size_t namelen; + int i; + + if (buflen < 0) + return -1; + + namelen = strlen(name); + if (namelen == 0) + return 0; + if (namelen > 255 || buflen < namelen+1) + return -1; + + i = 0; + while(i <= namelen) { + const char *e; + int llen, ii; + + e = strchr(&name[i], '.'); + if (e == NULL) + e = name + namelen; + llen = e - &name[i]; + if (llen == 0) { + if (*e) + return -1; + if (fqdn < 0) + return -1; + fqdn = 1; + break; + } + if (llen >= 0x40) + return -1; + buf[i] = llen; + for (ii = 0; ii < llen; ii++) { + if (!isascii(name[i+ii])) + return -1; + if (ii == 0 || ii == llen-1) { + if (!isalpha(name[i+ii]) && !isdigit(name[i+ii])) + return -1; + } else if (!isalnum(name[i+ii]) && name[i+ii] != '-') + return -1; + buf[i+ii+1] = isupper(name[i+ii]) ? tolower(name[i+ii]) : name[i+ii]; + } + i += llen + 1; + } + if (buflen < i + 1 + !(fqdn > 0)) + return -1; + buf[i++] = 0; + if (!(fqdn > 0)) + buf[i++] = 0; + return i; +} + +static int compare_dnsname(const char *s, size_t slen, + const char *n, size_t nlen) +{ + const char *s0 = s, *n0 = n; + int done = 0, retcode = 0; + if (slen < 1 || nlen < 1) + return -1; /* invalid length */ + /* simple case */ + if (slen == nlen && memcmp(s, n, slen) == 0) + return 0; + if (*(s0 + slen - 1) || *(n0 + nlen - 1)) + return -1; /* invalid termination */ + while (s < s0 + slen && n < n0 + nlen) { + if (*s >= 0x40 || *n >= 0x40) + return -1; /* DNS compression is not allowed here */ + if (s + *s + 1 > s0 + slen || n + *n + 1 > n0 + nlen) + return -1; /* overrun */ + if (*s == '\0') { + if (s == s0 + slen - 1) + break; /* FQDN */ + else if (s + 1 == s0 + slen - 1) + return retcode; /* truncated */ + else + return -1; /* more than one subject */ + } + if (!done) { + if (*n == '\0') { + if (n == n0 + nlen - 1) { + done = 1; /* FQDN */ + } else if (n + 1 == n0 + nlen - 1) { + retcode = 1; // trunc + done = 1; + } else + return -1; + } else { + if (*s != *n) { + done = 1; + retcode = 1; + } else { + if (memcmp(s+1, n+1, *s)) { + done = 1; + retcode = 1; + } + } + } + } + s += *s + 1; + n += done ? 0 : (*n + 1); + } + return retcode; +} + +static int nodeinfo_group(const char *dnsname, int namelen, + struct in6_addr *nigroup) +{ + MD5_CTX ctxt; + unsigned char digest[16]; + + if (!dnsname || !nigroup) + return -1; + + MD5_Init(&ctxt); + MD5_Update(&ctxt, dnsname, *dnsname); + MD5_Final(digest, &ctxt); + +#ifdef s6_addr32 + nigroup->s6_addr32[0] = htonl(0xff020000); + nigroup->s6_addr32[1] = 0; + nigroup->s6_addr32[2] = htonl(0x00000002); +#else + memset(nigroup, 0, sizeof(*nigroup)); + nigroup->s6_addr[ 0] = 0xff; + nigroup->s6_addr[ 1] = 0x02; + nigroup->s6_addr[11] = 0x02; +#endif + memcpy(&nigroup->s6_addr[12], digest, 4); + + return 0; +} + +/* ---------- */ +void init_nodeinfo_nodename(int forced) +{ + struct utsname newname; + int len; + int changed = 0; + + DEBUG(LOG_DEBUG, "%s()\n", __func__); + + uname(&newname); + changed = strcmp(newname.nodename, utsname.nodename); + + if (!changed && !forced) + return; + + memcpy(&utsname, &newname, sizeof(newname)); + + /* leave old group */ + if ((changed || forced) && !IN6_IS_ADDR_UNSPECIFIED(&nigroup.ipv6mr_multiaddr)) { + if (setsockopt(sock, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &nigroup, sizeof(nigroup)) < 0) { +#if ENABLE_DEBUG + char niaddrbuf[INET6_ADDRSTRLEN]; + if (inet_ntop(AF_INET6, &nigroup, niaddrbuf, sizeof(niaddrbuf)) == NULL) + strcpy(niaddrbuf, "???"); +#endif + DEBUG(LOG_WARNING, + "%s(): failed to leave group %s.\n", + __func__, niaddrbuf); + memset(&nigroup, 0, sizeof(nigroup)); + } + } + + len = encode_dnsname(uts_nodename, + nodename, + sizeof(nodename), + 0); + + /* setup ni reply */ + nodenamelen = len > 0 ? len : 0; + + /* setup ni group */ + if (changed || forced) { + if (nodenamelen) { + memset(&nigroup, 0, sizeof(nigroup)); + nodeinfo_group(nodename, len, &nigroup.ipv6mr_multiaddr); + nigroup.ipv6mr_interface = 0; + if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, &nigroup, sizeof(nigroup)) < 0) { +#if ENABLE_DEBUG + char niaddrbuf[INET6_ADDRSTRLEN]; + if (inet_ntop(AF_INET6, &nigroup, niaddrbuf, sizeof(niaddrbuf)) == NULL) + strcpy(niaddrbuf, "???"); +#endif + DEBUG(LOG_WARNING, + "%s(): failed to join group %s.\n", + __func__, niaddrbuf); + memset(&nigroup, 0, sizeof(nigroup)); + } + } else { + memset(&nigroup, 0, sizeof(nigroup)); + } + } + + return; +} + +/* ---------- */ +/* nodename */ +int pr_nodeinfo_nodename(CHECKANDFILL_ARGS) +{ + DEBUG(LOG_DEBUG, "%s()\n", __func__); + + if (subject) { + if (!nodenamelen || + compare_dnsname(subject, subjlen, + nodename, + nodenamelen)) + return 1; + if (subj_if) + *subj_if = p->pktinfo.ipi6_ifindex; + } + + if (reply) { + uint32_t ttl = 0; + + p->reply.ni_type = ICMP6_NI_REPLY; + p->reply.ni_code = ICMP6_NI_SUCCESS; + p->reply.ni_cksum = 0; + p->reply.ni_qtype = htons(NI_QTYPE_DNSNAME); + p->reply.ni_flags = 0; + + p->replydatalen = nodenamelen ? sizeof(ttl)+nodenamelen : 0; + p->replydata = nodenamelen ? ni_malloc(p->replydatalen) : NULL; + if (p->replydata) { + memcpy(p->replydata, &ttl, sizeof(ttl)); + memcpy(p->replydata + sizeof(ttl), &nodename, nodenamelen); + } + } + + return 0; +} + diff --git a/ping.c b/ping.c new file mode 100644 index 0000000..733477f --- /dev/null +++ b/ping.c @@ -0,0 +1,1702 @@ +/* + * Copyright (c) 1989 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Mike Muuss. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * P I N G . C + * + * Using the InterNet Control Message Protocol (ICMP) "ECHO" facility, + * measure round-trip-delays and packet loss across network paths. + * + * Author - + * Mike Muuss + * U. S. Army Ballistic Research Laboratory + * December, 1983 + * + * Status - + * Public Domain. Distribution Unlimited. + * Bugs - + * More statistics could always be gathered. + * If kernel does not support non-raw ICMP sockets, + * this program has to run SUID to ROOT or with + * net_cap_raw enabled. + */ + +#include "ping.h" + +#include +#include +#include +#ifndef WITHOUT_IFADDRS +#include +#endif + +#ifndef ICMP_FILTER +#define ICMP_FILTER 1 +struct icmp_filter { + __u32 data; +}; +#endif + +ping_func_set_st ping4_func_set = { + .send_probe = ping4_send_probe, + .receive_error_msg = ping4_receive_error_msg, + .parse_reply = ping4_parse_reply, + .install_filter = ping4_install_filter +}; + +#define MAXIPLEN 60 +#define MAXICMPLEN 76 +#define NROUTES 9 /* number of record route slots */ +#define TOS_MAX 255 /* 8-bit TOS field */ + +static int ts_type; +static int nroute = 0; +static __u32 route[10]; + +static struct sockaddr_in whereto; /* who to ping */ +static int optlen = 0; +static int settos = 0; /* Set TOS, Precendence or other QOS options */ + +static int broadcast_pings = 0; + +static void pr_options(unsigned char * cp, int hlen); +static void pr_iph(struct iphdr *ip); +static void usage(void) __attribute__((noreturn)); +static unsigned short in_cksum(const unsigned short *addr, int len, unsigned short salt); +static void pr_icmph(__u8 type, __u8 code, __u32 info, struct icmphdr *icp); +static int parsetos(char *str); +static int parseflow(char *str); + +static struct { + struct cmsghdr cm; + struct in_pktinfo ipi; +} cmsg = { {sizeof(struct cmsghdr) + sizeof(struct in_pktinfo), SOL_IP, IP_PKTINFO}, + {0, }}; +int cmsg_len; + +static struct sockaddr_in source = { .sin_family = AF_INET }; +char *device; +int pmtudisc = -1; + +static void create_socket(socket_st *sock, int family, int socktype, int protocol, int requisite) +{ + int do_fallback = 0; + + errno = 0; + + assert(sock->fd == -1); + assert(socktype == SOCK_DGRAM || socktype == SOCK_RAW); + + /* Attempt to create a ping socket if requested. Attempt to create a raw + * socket otherwise or as a fallback. Well known errno values follow. + * + * 1) EACCES + * + * Kernel returns EACCES for all ping socket creation attempts when the + * user isn't allowed to use ping socket. A range of group ids is + * configured using the `net.ipv4.ping_group_range` sysctl. Fallback + * to raw socket is necessary. + * + * Kernel returns EACCES for all raw socket creation attempts when the + * proces doesn't have the `CAP_NET_RAW` capability. + * + * 2) EAFNOSUPPORT + * + * Kernel returns EAFNOSUPPORT for IPv6 ping or raw socket creation + * attempts when run with IPv6 support disabled (e.g. via `ipv6.disable=1` + * kernel command-line option. + * + * https://github.com/iputils/iputils/issues/32 + * + * OpenVZ 2.6.32-042stab113.11 and possibly other older kernels return + * EAFNOSUPPORT for all IPv4 ping socket creation attempts due to lack + * of support in the kernel. Fallback to raw socket is necessary. + * + * https://github.com/iputils/iputils/issues/54 + * + * 3) EPROTONOSUPPORT + * + * OpenVZ 2.6.32-042stab113.11 and possibly other older kernels return + * EPROTONOSUPPORT for all IPv6 ping socket creation attempts due to lack + * of support in the kernel. Fallback to raw socket is necessary. + * + * https://github.com/iputils/iputils/issues/54 + * + */ + if (socktype == SOCK_DGRAM) + sock->fd = socket(family, socktype, protocol); + + /* Kernel doesn't support ping sockets. */ + if (sock->fd == -1 && errno == EAFNOSUPPORT && family == AF_INET) + do_fallback = 1; + if (sock->fd == -1 && errno == EPROTONOSUPPORT && family == AF_INET6) + do_fallback = 1; + + /* User is not allowed to use ping sockets. */ + if (sock->fd == -1 && errno == EACCES) + do_fallback = 1; + + if (socktype == SOCK_RAW || do_fallback) { + socktype = SOCK_RAW; + sock->fd = socket(family, SOCK_RAW, protocol); + } + + if (sock->fd == -1) { + /* Report error related to disabled IPv6 only when IPv6 also failed or in + * verbose mode. Report other errors always. + */ + if ((errno == EAFNOSUPPORT && socktype == AF_INET6) || options & F_VERBOSE || requisite) + fprintf(stderr, "ping: socket: %s\n", strerror(errno)); + if (requisite) + exit(2); + } else + sock->socktype = socktype; +} + +static void set_socket_option(socket_st *sock, int level, int optname, const void *optval, socklen_t optlen) +{ + if (sock->fd == -1) + return; + + if (setsockopt(sock->fd, level, optname, optval, optlen) == -1) { + fprintf(stderr, "ping: setsockopt: %s\n", strerror(errno)); + exit(2); + } +} + +int +main(int argc, char **argv) +{ + struct addrinfo hints = { .ai_family = AF_UNSPEC, .ai_protocol = IPPROTO_UDP, .ai_socktype = SOCK_DGRAM, .ai_flags = getaddrinfo_flags }; + struct addrinfo *result, *ai; + int status; + int ch; + socket_st sock4 = { .fd = -1 }; + socket_st sock6 = { .fd = -1 }; + char *target; + + limit_capabilities(); + +#ifdef USE_IDN + setlocale(LC_ALL, ""); +#endif + if (!strcmp(setlocale(LC_ALL, NULL), "C")) + hints.ai_flags &= ~ AI_CANONIDN; + + /* Support being called using `ping4` or `ping6` symlinks */ + if (argv[0][strlen(argv[0])-1] == '4') + hints.ai_family = AF_INET; + else if (argv[0][strlen(argv[0])-1] == '6') + hints.ai_family = AF_INET6; + + /* Parse command line options */ + while ((ch = getopt(argc, argv, "h?" "4bRT:" "6F:N:" "aABc:dDfi:I:l:Lm:M:nOp:qQ:rs:S:t:UvVw:W:")) != EOF) { + switch(ch) { + /* IPv4 specific options */ + case '4': + if (hints.ai_family != AF_UNSPEC) { + fprintf(stderr, "ping: Only one -4 or -6 option may be specified\n"); + exit(2); + } + hints.ai_family = AF_INET; + break; + case 'b': + broadcast_pings = 1; + break; + case 'R': + if (options & F_TIMESTAMP) { + fprintf(stderr, "Only one of -T or -R may be used\n"); + exit(2); + } + options |= F_RROUTE; + break; + case 'T': + if (options & F_RROUTE) { + fprintf(stderr, "Only one of -T or -R may be used\n"); + exit(2); + } + options |= F_TIMESTAMP; + if (strcmp(optarg, "tsonly") == 0) + ts_type = IPOPT_TS_TSONLY; + else if (strcmp(optarg, "tsandaddr") == 0) + ts_type = IPOPT_TS_TSANDADDR; + else if (strcmp(optarg, "tsprespec") == 0) + ts_type = IPOPT_TS_PRESPEC; + else { + fprintf(stderr, "Invalid timestamp type\n"); + exit(2); + } + break; + /* IPv6 specific options */ + case '6': + if (hints.ai_family != AF_UNSPEC) { + fprintf(stderr, "ping: Only one -4 or -6 option may be specified\n"); + exit(2); + } + hints.ai_family = AF_INET6; + break; + case 'F': + flowlabel = parseflow(optarg); + options |= F_FLOWINFO; + break; + case 'N': + if (niquery_option_handler(optarg) < 0) { + ping6_usage(0); + exit(2); + } + hints.ai_socktype = SOCK_RAW; + break; + /* Common options */ + case 'a': + options |= F_AUDIBLE; + break; + case 'A': + options |= F_ADAPTIVE; + break; + case 'B': + options |= F_STRICTSOURCE; + break; + case 'c': + npackets = atoi(optarg); + if (npackets <= 0) { + fprintf(stderr, "ping: bad number of packets to transmit.\n"); + exit(2); + } + break; + case 'd': + options |= F_SO_DEBUG; + break; + case 'D': + options |= F_PTIMEOFDAY; + break; + case 'i': + { + double dbl; + char *ep; + + errno = 0; +#ifdef USE_IDN + setlocale(LC_ALL, "C"); +#endif + dbl = strtod(optarg, &ep); +#ifdef USE_IDN + setlocale(LC_ALL, ""); +#endif + + if (errno || *ep != '\0' || + !finite(dbl) || dbl < 0.0 || dbl >= (double)INT_MAX / 1000 - 1.0) { + fprintf(stderr, "ping: bad timing interval\n"); + exit(2); + } + + interval = (int)(dbl * 1000); + + options |= F_INTERVAL; + break; + } + case 'I': + /* IPv6 */ + if (strchr(optarg, ':')) { + char *p, *addr = strdup(optarg); + + if (!addr) { + fprintf(stderr, "ping: out of memory\n"); + exit(2); + } + + p = strchr(addr, SCOPE_DELIMITER); + if (p) { + *p = '\0'; + device = optarg + (p - addr) + 1; + } + + if (inet_pton(AF_INET6, addr, (char*)&source6.sin6_addr) <= 0) { + fprintf(stderr, "ping: invalid source address %s\n", optarg); + exit(2); + } + + options |= F_STRICTSOURCE; + + free(addr); + } else if (inet_pton(AF_INET, optarg, &source.sin_addr) > 0) { + options |= F_STRICTSOURCE; + } else { + device = optarg; + } + break; + case 'l': + preload = atoi(optarg); + if (preload <= 0) { + fprintf(stderr, "ping: bad preload value, should be 1..%d\n", MAX_DUP_CHK); + exit(2); + } + if (preload > MAX_DUP_CHK) + preload = MAX_DUP_CHK; + if (uid && preload > 3) { + fprintf(stderr, "ping: cannot set preload to value > 3\n"); + exit(2); + } + break; + case 'L': + options |= F_NOLOOP; + break; + case 'm': + { + char *endp; + mark = (int)strtoul(optarg, &endp, 10); + if (mark < 0 || *endp != '\0') { + fprintf(stderr, "mark cannot be negative\n"); + exit(2); + } + options |= F_MARK; + break; + } + case 'M': + if (strcmp(optarg, "do") == 0) + pmtudisc = IP_PMTUDISC_DO; + else if (strcmp(optarg, "dont") == 0) + pmtudisc = IP_PMTUDISC_DONT; + else if (strcmp(optarg, "want") == 0) + pmtudisc = IP_PMTUDISC_WANT; + else { + fprintf(stderr, "ping: wrong value for -M: do, dont, want are valid ones.\n"); + exit(2); + } + break; + case 'n': + options |= F_NUMERIC; + break; + case 'O': + options |= F_OUTSTANDING; + break; + case 'f': + /* avoid `getaddrinfo()` during flood */ + options |= F_FLOOD | F_NUMERIC; + setbuf(stdout, (char *)NULL); + break; + case 'p': + options |= F_PINGFILLED; + fill(optarg, outpack, sizeof(outpack)); + break; + case 'q': + options |= F_QUIET; + break; + case 'Q': + settos = parsetos(optarg); /* IPv4 */ + tclass = settos; /* IPv6 */ + break; + case 'r': + options |= F_SO_DONTROUTE; + break; + case 's': + datalen = atoi(optarg); + if (datalen < 0) { + fprintf(stderr, "ping: illegal negative packet size %d.\n", datalen); + exit(2); + } + if (datalen > MAXPACKET - 8) { + fprintf(stderr, "ping: packet size too large: %d\n", + datalen); + exit(2); + } + break; + case 'S': + sndbuf = atoi(optarg); + if (sndbuf <= 0) { + fprintf(stderr, "ping: bad sndbuf value.\n"); + exit(2); + } + break; + case 't': + options |= F_TTL; + ttl = atoi(optarg); + if (ttl < 0 || ttl > 255) { + fprintf(stderr, "ping: ttl %u out of range\n", ttl); + exit(2); + } + break; + case 'U': + options |= F_LATENCY; + break; + case 'v': + options |= F_VERBOSE; + break; + case 'V': + printf("ping utility, iputils-%s\n", SNAPSHOT); + exit(0); + case 'w': + deadline = atoi(optarg); + if (deadline < 0) { + fprintf(stderr, "ping: bad wait time.\n"); + exit(2); + } + break; + case 'W': + lingertime = atoi(optarg); + if (lingertime < 0 || lingertime > INT_MAX/1000000) { + fprintf(stderr, "ping: bad linger time.\n"); + exit(2); + } + lingertime *= 1000; + break; + default: + usage(); + break; + } + } + + argc -= optind; + argv += optind; + + if (!argc) + usage(); + + target = argv[argc-1]; + + /* Create sockets */ + enable_capability_raw(); + if (hints.ai_family != AF_INET6) + create_socket(&sock4, AF_INET, hints.ai_socktype, IPPROTO_ICMP, hints.ai_family == AF_INET); + if (hints.ai_family != AF_INET) { + create_socket(&sock6, AF_INET6, hints.ai_socktype, IPPROTO_ICMPV6, sock4.fd == -1); + /* This may not be needed if both protocol versions always had the same value, but + * since I don't know that, it's better to be safe than sorry. */ + pmtudisc = pmtudisc == IP_PMTUDISC_DO ? IPV6_PMTUDISC_DO : + pmtudisc == IP_PMTUDISC_DONT ? IPV6_PMTUDISC_DONT : + pmtudisc == IP_PMTUDISC_WANT ? IPV6_PMTUDISC_WANT : pmtudisc; + } + disable_capability_raw(); + + /* Limit address family on single-protocol systems */ + if (hints.ai_family == AF_UNSPEC) { + if (sock4.fd == -1) + hints.ai_family = AF_INET6; + else if (sock6.fd == -1) + hints.ai_family = AF_INET; + } + + /* Set socket options */ + if (settos) + set_socket_option(&sock4, IPPROTO_IP, IP_TOS, &settos, sizeof settos); + if (tclass) + set_socket_option(&sock6, IPPROTO_IPV6, IPV6_TCLASS, &tclass, sizeof tclass); + + status = getaddrinfo(target, NULL, &hints, &result); + if (status) { + fprintf(stderr, "ping: %s: %s\n", target, gai_strerror(status)); + exit(2); + } + + for (ai = result; ai; ai = ai->ai_next) { + switch (ai->ai_family) { + case AF_INET: + status = ping4_run(argc, argv, ai, &sock4); + break; + case AF_INET6: + status = ping6_run(argc, argv, ai, &sock6); + break; + default: + fprintf(stderr, "ping: unknown protocol family: %d\n", ai->ai_family); + exit(2); + } + + if (status == 0) + break; + } + + freeaddrinfo(result); + + return status; +} + +int ping4_run(int argc, char **argv, struct addrinfo *ai, socket_st *sock) +{ + static const struct addrinfo hints = { .ai_family = AF_INET, .ai_protocol = IPPROTO_UDP, .ai_flags = getaddrinfo_flags }; + int hold, packlen; + unsigned char *packet; + char *target; + char hnamebuf[NI_MAXHOST]; + unsigned char rspace[3 + 4 * NROUTES + 1]; /* record route space */ + __u32 *tmp_rspace; + + if (argc > 1) { + if (options & F_RROUTE) + usage(); + else if (options & F_TIMESTAMP) { + if (ts_type != IPOPT_TS_PRESPEC) + usage(); + if (argc > 5) + usage(); + } else { + if (argc > 10) + usage(); + options |= F_SOURCEROUTE; + } + } + while (argc > 0) { + target = *argv; + + memset((char *)&whereto, 0, sizeof(whereto)); + whereto.sin_family = AF_INET; + if (inet_aton(target, &whereto.sin_addr) == 1) { + hostname = target; + if (argc == 1) + options |= F_NUMERIC; + } else { + struct addrinfo *result = NULL; + int status; + + if (argc > 1 || !ai) { + status = getaddrinfo(target, NULL, &hints, &result); + if (status) { + fprintf(stderr, "ping: %s: %s\n", target, gai_strerror(status)); + exit(2); + } + ai = result; + } + + memcpy(&whereto, ai->ai_addr, sizeof whereto); + memset(hnamebuf, 0, sizeof hnamebuf); + if (ai->ai_canonname) + strncpy(hnamebuf, ai->ai_canonname, sizeof hnamebuf - 1); + hostname = hnamebuf; + + if (result) + freeaddrinfo(result); + } + if (argc > 1) + route[nroute++] = whereto.sin_addr.s_addr; + argc--; + argv++; + } + + if (source.sin_addr.s_addr == 0) { + socklen_t alen; + struct sockaddr_in dst = whereto; + int probe_fd = socket(AF_INET, SOCK_DGRAM, 0); + + if (probe_fd < 0) { + perror("socket"); + exit(2); + } + if (device) { + struct ifreq ifr; + int i; + int fds[2] = {probe_fd, sock->fd}; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, device, IFNAMSIZ-1); + + for (i = 0; i < 2; i++) { + int fd = fds[i]; + int rc; + enable_capability_raw(); + rc = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, device, strlen(device)+1); + disable_capability_raw(); + + if (rc == -1) { + if (IN_MULTICAST(ntohl(dst.sin_addr.s_addr))) { + struct ip_mreqn imr; + if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) { + fprintf(stderr, "ping: unknown iface %s\n", device); + exit(2); + } + memset(&imr, 0, sizeof(imr)); + imr.imr_ifindex = ifr.ifr_ifindex; + if (setsockopt(fd, SOL_IP, IP_MULTICAST_IF, &imr, sizeof(imr)) == -1) { + perror("ping: IP_MULTICAST_IF"); + exit(2); + } + } else { + perror("ping: SO_BINDTODEVICE"); + exit(2); + } + } + } + } + + if (settos && + setsockopt(probe_fd, IPPROTO_IP, IP_TOS, (char *)&settos, sizeof(int)) < 0) + perror("Warning: error setting QOS sockopts"); + + dst.sin_port = htons(1025); + if (nroute) + dst.sin_addr.s_addr = route[0]; + if (connect(probe_fd, (struct sockaddr*)&dst, sizeof(dst)) == -1) { + if (errno == EACCES) { + if (broadcast_pings == 0) { + fprintf(stderr, + "Do you want to ping broadcast? Then -b. If not, check your local firewall rules.\n"); + exit(2); + } + fprintf(stderr, "WARNING: pinging broadcast address\n"); + if (setsockopt(probe_fd, SOL_SOCKET, SO_BROADCAST, + &broadcast_pings, sizeof(broadcast_pings)) < 0) { + perror ("can't set broadcasting"); + exit(2); + } + if (connect(probe_fd, (struct sockaddr*)&dst, sizeof(dst)) == -1) { + perror("connect"); + exit(2); + } + } else { + perror("connect"); + exit(2); + } + } + alen = sizeof(source); + if (getsockname(probe_fd, (struct sockaddr*)&source, &alen) == -1) { + perror("getsockname"); + exit(2); + } + source.sin_port = 0; + +#ifndef WITHOUT_IFADDRS + if (device) { + struct ifaddrs *ifa0, *ifa; + int ret; + + ret = getifaddrs(&ifa0); + if (ret) { + fprintf(stderr, "gatifaddrs() failed.\n"); + exit(2); + } + for (ifa = ifa0; ifa; ifa = ifa->ifa_next) { + if (!ifa->ifa_addr || ifa->ifa_addr->sa_family != AF_INET) + continue; + if (!strncmp(ifa->ifa_name, device, sizeof(device) - 1) && + !memcmp(&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr, + &source.sin_addr, sizeof(source.sin_addr))) + break; + } + freeifaddrs(ifa0); + if (!ifa) + fprintf(stderr, "ping: Warning: source address might be selected on device other than %s.\n", device); + } +#endif + close(probe_fd); + } while (0); + + if (whereto.sin_addr.s_addr == 0) + whereto.sin_addr.s_addr = source.sin_addr.s_addr; + + if (device) { + struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, device, IFNAMSIZ-1); + if (ioctl(sock->fd, SIOCGIFINDEX, &ifr) < 0) { + fprintf(stderr, "ping: unknown iface %s\n", device); + exit(2); + } + cmsg.ipi.ipi_ifindex = ifr.ifr_ifindex; + cmsg_len = sizeof(cmsg); + } + + if (broadcast_pings || IN_MULTICAST(ntohl(whereto.sin_addr.s_addr))) { + if (uid) { + if (interval < 1000) { + fprintf(stderr, "ping: broadcast ping with too short interval.\n"); + exit(2); + } + if (pmtudisc >= 0 && pmtudisc != IP_PMTUDISC_DO) { + fprintf(stderr, "ping: broadcast ping does not fragment.\n"); + exit(2); + } + } + if (pmtudisc < 0) + pmtudisc = IP_PMTUDISC_DO; + } + + if (pmtudisc >= 0) { + if (setsockopt(sock->fd, SOL_IP, IP_MTU_DISCOVER, &pmtudisc, sizeof pmtudisc) == -1) { + perror("ping: IP_MTU_DISCOVER"); + exit(2); + } + } + + if ((options&F_STRICTSOURCE) && + bind(sock->fd, (struct sockaddr *) &source, sizeof source) == -1) { + perror("bind"); + exit(2); + } + + if (sock->socktype == SOCK_RAW) { + struct icmp_filter filt; + filt.data = ~((1<fd, SOL_RAW, ICMP_FILTER, &filt, sizeof filt) == -1) + perror("WARNING: setsockopt(ICMP_FILTER)"); + } + + hold = 1; + if (setsockopt(sock->fd, SOL_IP, IP_RECVERR, &hold, sizeof hold)) + fprintf(stderr, "WARNING: your kernel is veeery old. No problems.\n"); + + if (sock->socktype == SOCK_DGRAM) { + if (setsockopt(sock->fd, SOL_IP, IP_RECVTTL, &hold, sizeof hold)) + perror("WARNING: setsockopt(IP_RECVTTL)"); + if (setsockopt(sock->fd, SOL_IP, IP_RETOPTS, &hold, sizeof hold)) + perror("WARNING: setsockopt(IP_RETOPTS)"); + } + + /* record route option */ + if (options & F_RROUTE) { + memset(rspace, 0, sizeof(rspace)); + rspace[0] = IPOPT_NOP; + rspace[1+IPOPT_OPTVAL] = IPOPT_RR; + rspace[1+IPOPT_OLEN] = sizeof(rspace)-1; + rspace[1+IPOPT_OFFSET] = IPOPT_MINOFF; + optlen = 40; + if (setsockopt(sock->fd, IPPROTO_IP, IP_OPTIONS, rspace, sizeof rspace) < 0) { + perror("ping: record route"); + exit(2); + } + } + if (options & F_TIMESTAMP) { + memset(rspace, 0, sizeof(rspace)); + rspace[0] = IPOPT_TIMESTAMP; + rspace[1] = (ts_type==IPOPT_TS_TSONLY ? 40 : 36); + rspace[2] = 5; + rspace[3] = ts_type; + if (ts_type == IPOPT_TS_PRESPEC) { + int i; + rspace[1] = 4+nroute*8; + for (i = 0; i < nroute; i++) { + tmp_rspace = (__u32*)&rspace[4+i*8]; + *tmp_rspace = route[i]; + } + } + if (setsockopt(sock->fd, IPPROTO_IP, IP_OPTIONS, rspace, rspace[1]) < 0) { + rspace[3] = 2; + if (setsockopt(sock->fd, IPPROTO_IP, IP_OPTIONS, rspace, rspace[1]) < 0) { + perror("ping: ts option"); + exit(2); + } + } + optlen = 40; + } + if (options & F_SOURCEROUTE) { + int i; + memset(rspace, 0, sizeof(rspace)); + rspace[0] = IPOPT_NOOP; + rspace[1+IPOPT_OPTVAL] = (options & F_SO_DONTROUTE) ? IPOPT_SSRR + : IPOPT_LSRR; + rspace[1+IPOPT_OLEN] = 3 + nroute*4; + rspace[1+IPOPT_OFFSET] = IPOPT_MINOFF; + for (i = 0; i < nroute; i++) { + tmp_rspace = (__u32*)&rspace[4+i*4]; + *tmp_rspace = route[i]; + } + + if (setsockopt(sock->fd, IPPROTO_IP, IP_OPTIONS, rspace, 4 + nroute*4) < 0) { + perror("ping: record route"); + exit(2); + } + optlen = 40; + } + + /* Estimate memory eaten by single packet. It is rough estimate. + * Actually, for small datalen's it depends on kernel side a lot. */ + hold = datalen + 8; + hold += ((hold+511)/512)*(optlen + 20 + 16 + 64 + 160); + sock_setbufs(sock, hold); + + if (broadcast_pings) { + if (setsockopt(sock->fd, SOL_SOCKET, SO_BROADCAST, &broadcast_pings, sizeof broadcast_pings) < 0) { + perror ("ping: can't set broadcasting"); + exit(2); + } + } + + if (options & F_NOLOOP) { + int loop = 0; + if (setsockopt(sock->fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof loop) == -1) { + perror ("ping: can't disable multicast loopback"); + exit(2); + } + } + if (options & F_TTL) { + int ittl = ttl; + if (setsockopt(sock->fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof ttl) == -1) { + perror ("ping: can't set multicast time-to-live"); + exit(2); + } + if (setsockopt(sock->fd, IPPROTO_IP, IP_TTL, &ittl, sizeof ittl) == -1) { + perror ("ping: can't set unicast time-to-live"); + exit(2); + } + } + + if (datalen > 0xFFFF - 8 - optlen - 20) { + fprintf(stderr, "Error: packet size %d is too large. Maximum is %d\n", datalen, 0xFFFF-8-20-optlen); + exit(2); + } + + if (datalen >= sizeof(struct timeval)) /* can we time transfer */ + timing = 1; + packlen = datalen + MAXIPLEN + MAXICMPLEN; + if (!(packet = (unsigned char *)malloc((unsigned int)packlen))) { + fprintf(stderr, "ping: out of memory.\n"); + exit(2); + } + + printf("PING %s (%s) ", hostname, inet_ntoa(whereto.sin_addr)); + if (device || (options&F_STRICTSOURCE)) + printf("from %s %s: ", inet_ntoa(source.sin_addr), device ?: ""); + printf("%d(%d) bytes of data.\n", datalen, datalen+8+optlen+20); + + setup(sock); + + main_loop(&ping4_func_set, sock, packet, packlen); +} + + +int ping4_receive_error_msg(socket_st *sock) +{ + int res; + char cbuf[512]; + struct iovec iov; + struct msghdr msg; + struct cmsghdr *cmsg; + struct sock_extended_err *e; + struct icmphdr icmph; + struct sockaddr_in target; + int net_errors = 0; + int local_errors = 0; + int saved_errno = errno; + + iov.iov_base = &icmph; + iov.iov_len = sizeof(icmph); + msg.msg_name = (void*)⌖ + msg.msg_namelen = sizeof(target); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_flags = 0; + msg.msg_control = cbuf; + msg.msg_controllen = sizeof(cbuf); + + res = recvmsg(sock->fd, &msg, MSG_ERRQUEUE|MSG_DONTWAIT); + if (res < 0) + goto out; + + e = NULL; + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level == SOL_IP) { + if (cmsg->cmsg_type == IP_RECVERR) + e = (struct sock_extended_err *)CMSG_DATA(cmsg); + } + } + if (e == NULL) + abort(); + + if (e->ee_origin == SO_EE_ORIGIN_LOCAL) { + local_errors++; + if (options & F_QUIET) + goto out; + if (options & F_FLOOD) + write_stdout("E", 1); + else if (e->ee_errno != EMSGSIZE) + fprintf(stderr, "ping: local error: %s\n", strerror(e->ee_errno)); + else + fprintf(stderr, "ping: local error: Message too long, mtu=%u\n", e->ee_info); + nerrors++; + } else if (e->ee_origin == SO_EE_ORIGIN_ICMP) { + struct sockaddr_in *sin = (struct sockaddr_in*)(e+1); + + if (res < sizeof(icmph) || + target.sin_addr.s_addr != whereto.sin_addr.s_addr || + icmph.type != ICMP_ECHO || + !is_ours(sock, icmph.un.echo.id)) { + /* Not our error, not an error at all. Clear. */ + saved_errno = 0; + goto out; + } + + acknowledge(ntohs(icmph.un.echo.sequence)); + + net_errors++; + nerrors++; + if (options & F_QUIET) + goto out; + if (options & F_FLOOD) { + write_stdout("\bE", 2); + } else { + print_timestamp(); + printf("From %s icmp_seq=%u ", pr_addr(sin, sizeof *sin), ntohs(icmph.un.echo.sequence)); + pr_icmph(e->ee_type, e->ee_code, e->ee_info, NULL); + fflush(stdout); + } + } + +out: + errno = saved_errno; + return net_errors ? : -local_errors; +} + +/* + * pinger -- + * Compose and transmit an ICMP ECHO REQUEST packet. The IP packet + * will be added on by the kernel. The ID field is our UNIX process ID, + * and the sequence number is an ascending integer. The first 8 bytes + * of the data portion are used to hold a UNIX "timeval" struct in VAX + * byte-order, to compute the round-trip time. + */ +int ping4_send_probe(socket_st *sock, void *packet, unsigned packet_size) +{ + struct icmphdr *icp; + int cc; + int i; + + icp = (struct icmphdr *)packet; + icp->type = ICMP_ECHO; + icp->code = 0; + icp->checksum = 0; + icp->un.echo.sequence = htons(ntransmitted+1); + icp->un.echo.id = ident; /* ID */ + + rcvd_clear(ntransmitted+1); + + if (timing) { + if (options&F_LATENCY) { + struct timeval tmp_tv; + gettimeofday(&tmp_tv, NULL); + memcpy(icp+1, &tmp_tv, sizeof(tmp_tv)); + } else { + memset(icp+1, 0, sizeof(struct timeval)); + } + } + + cc = datalen + 8; /* skips ICMP portion */ + + /* compute ICMP checksum here */ + icp->checksum = in_cksum((unsigned short *)icp, cc, 0); + + if (timing && !(options&F_LATENCY)) { + struct timeval tmp_tv; + gettimeofday(&tmp_tv, NULL); + memcpy(icp+1, &tmp_tv, sizeof(tmp_tv)); + icp->checksum = in_cksum((unsigned short *)&tmp_tv, sizeof(tmp_tv), ~icp->checksum); + } + + i = sendto(sock->fd, icp, cc, 0, (struct sockaddr*)&whereto, sizeof(whereto)); + + return (cc == i ? 0 : i); +} + +/* + * parse_reply -- + * Print out the packet, if it came from us. This logic is necessary + * because ALL readers of the ICMP socket get a copy of ALL ICMP packets + * which arrive ('tis only fair). This permits multiple copies of this + * program to be run without having intermingled output (or statistics!). + */ +static +void pr_echo_reply(__u8 *_icp, int len) +{ + struct icmphdr *icp = (struct icmphdr *)_icp; + printf(" icmp_seq=%u", ntohs(icp->un.echo.sequence)); +} + +int +ping4_parse_reply(struct socket_st *sock, struct msghdr *msg, int cc, void *addr, struct timeval *tv) +{ + struct sockaddr_in *from = addr; + __u8 *buf = msg->msg_iov->iov_base; + struct icmphdr *icp; + struct iphdr *ip; + int hlen; + int csfailed; + struct cmsghdr *cmsg; + int ttl; + __u8 *opts, *tmp_ttl; + int optlen; + + /* Check the IP header */ + ip = (struct iphdr *)buf; + if (sock->socktype == SOCK_RAW) { + hlen = ip->ihl*4; + if (cc < hlen + 8 || ip->ihl < 5) { + if (options & F_VERBOSE) + fprintf(stderr, "ping: packet too short (%d bytes) from %s\n", cc, + pr_addr(from, sizeof *from)); + return 1; + } + ttl = ip->ttl; + opts = buf + sizeof(struct iphdr); + optlen = hlen - sizeof(struct iphdr); + } else { + hlen = 0; + ttl = 0; + opts = buf; + optlen = 0; + for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { + if (cmsg->cmsg_level != SOL_IP) + continue; + if (cmsg->cmsg_type == IP_TTL) { + if (cmsg->cmsg_len < sizeof(int)) + continue; + tmp_ttl = (__u8 *) CMSG_DATA(cmsg); + ttl = (int)*tmp_ttl; + } else if (cmsg->cmsg_type == IP_RETOPTS) { + opts = (__u8 *) CMSG_DATA(cmsg); + optlen = cmsg->cmsg_len; + } + } + } + + /* Now the ICMP part */ + cc -= hlen; + icp = (struct icmphdr *)(buf + hlen); + csfailed = in_cksum((unsigned short *)icp, cc, 0); + + if (icp->type == ICMP_ECHOREPLY) { + if (!is_ours(sock, icp->un.echo.id)) + return 1; /* 'Twas not our ECHO */ + if (!contains_pattern_in_payload((__u8*)(icp+1))) + return 1; /* 'Twas really not our ECHO */ + if (gather_statistics((__u8*)icp, sizeof(*icp), cc, + ntohs(icp->un.echo.sequence), + ttl, csfailed, tv, pr_addr(from, sizeof *from), + pr_echo_reply)) { + fflush(stdout); + return 0; + } + } else { + /* We fall here when a redirect or source quench arrived. */ + + switch (icp->type) { + case ICMP_ECHO: + /* MUST NOT */ + return 1; + case ICMP_SOURCE_QUENCH: + case ICMP_REDIRECT: + case ICMP_DEST_UNREACH: + case ICMP_TIME_EXCEEDED: + case ICMP_PARAMETERPROB: + { + struct iphdr * iph = (struct iphdr *)(&icp[1]); + struct icmphdr *icp1 = (struct icmphdr*)((unsigned char *)iph + iph->ihl*4); + int error_pkt; + if (cc < 8+sizeof(struct iphdr)+8 || + cc < 8+iph->ihl*4+8) + return 1; + if (icp1->type != ICMP_ECHO || + iph->daddr != whereto.sin_addr.s_addr || + !is_ours(sock, icp1->un.echo.id)) + return 1; + error_pkt = (icp->type != ICMP_REDIRECT && + icp->type != ICMP_SOURCE_QUENCH); + if (error_pkt) { + acknowledge(ntohs(icp1->un.echo.sequence)); + return 0; + } + if (options & (F_QUIET | F_FLOOD)) + return 1; + print_timestamp(); + printf("From %s: icmp_seq=%u ", + pr_addr(from, sizeof *from), + ntohs(icp1->un.echo.sequence)); + if (csfailed) + printf("(BAD CHECKSUM)"); + pr_icmph(icp->type, icp->code, ntohl(icp->un.gateway), icp); + return 1; + } + default: + /* MUST NOT */ + break; + } + if ((options & F_FLOOD) && !(options & (F_VERBOSE|F_QUIET))) { + if (!csfailed) + write_stdout("!E", 2); + else + write_stdout("!EC", 3); + return 0; + } + if (!(options & F_VERBOSE) || uid) + return 0; + if (options & F_PTIMEOFDAY) { + struct timeval recv_time; + gettimeofday(&recv_time, NULL); + printf("%lu.%06lu ", (unsigned long)recv_time.tv_sec, (unsigned long)recv_time.tv_usec); + } + printf("From %s: ", pr_addr(from, sizeof *from)); + if (csfailed) { + printf("(BAD CHECKSUM)\n"); + return 0; + } + pr_icmph(icp->type, icp->code, ntohl(icp->un.gateway), icp); + return 0; + } + + if (options & F_AUDIBLE) { + putchar('\a'); + if(options & F_FLOOD) + fflush(stdout); + } + if (!(options & F_FLOOD)) { + pr_options(opts, optlen + sizeof(struct iphdr)); + + putchar('\n'); + fflush(stdout); + } + return 0; +} + + +#if BYTE_ORDER == LITTLE_ENDIAN +# define ODDBYTE(v) (v) +#elif BYTE_ORDER == BIG_ENDIAN +# define ODDBYTE(v) ((unsigned short)(v) << 8) +#else +# define ODDBYTE(v) htons((unsigned short)(v) << 8) +#endif + +unsigned short +in_cksum(const unsigned short *addr, register int len, unsigned short csum) +{ + register int nleft = len; + const unsigned short *w = addr; + register unsigned short answer; + register int sum = csum; + + /* + * Our algorithm is simple, using a 32 bit accumulator (sum), + * we add sequential 16 bit words to it, and at the end, fold + * back all the carry bits from the top 16 bits into the lower + * 16 bits. + */ + while (nleft > 1) { + sum += *w++; + nleft -= 2; + } + + /* mop up an odd byte, if necessary */ + if (nleft == 1) + sum += ODDBYTE(*(unsigned char *)w); /* le16toh() may be unavailable on old systems */ + + /* + * add back carry outs from top 16 bits to low 16 bits + */ + sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ + sum += (sum >> 16); /* add carry */ + answer = ~sum; /* truncate to 16 bits */ + return (answer); +} + +/* + * pr_icmph -- + * Print a descriptive string about an ICMP header. + */ +void pr_icmph(__u8 type, __u8 code, __u32 info, struct icmphdr *icp) +{ + switch(type) { + case ICMP_ECHOREPLY: + printf("Echo Reply\n"); + /* XXX ID + Seq + Data */ + break; + case ICMP_DEST_UNREACH: + switch(code) { + case ICMP_NET_UNREACH: + printf("Destination Net Unreachable\n"); + break; + case ICMP_HOST_UNREACH: + printf("Destination Host Unreachable\n"); + break; + case ICMP_PROT_UNREACH: + printf("Destination Protocol Unreachable\n"); + break; + case ICMP_PORT_UNREACH: + printf("Destination Port Unreachable\n"); + break; + case ICMP_FRAG_NEEDED: + printf("Frag needed and DF set (mtu = %u)\n", info); + break; + case ICMP_SR_FAILED: + printf("Source Route Failed\n"); + break; + case ICMP_NET_UNKNOWN: + printf("Destination Net Unknown\n"); + break; + case ICMP_HOST_UNKNOWN: + printf("Destination Host Unknown\n"); + break; + case ICMP_HOST_ISOLATED: + printf("Source Host Isolated\n"); + break; + case ICMP_NET_ANO: + printf("Destination Net Prohibited\n"); + break; + case ICMP_HOST_ANO: + printf("Destination Host Prohibited\n"); + break; + case ICMP_NET_UNR_TOS: + printf("Destination Net Unreachable for Type of Service\n"); + break; + case ICMP_HOST_UNR_TOS: + printf("Destination Host Unreachable for Type of Service\n"); + break; + case ICMP_PKT_FILTERED: + printf("Packet filtered\n"); + break; + case ICMP_PREC_VIOLATION: + printf("Precedence Violation\n"); + break; + case ICMP_PREC_CUTOFF: + printf("Precedence Cutoff\n"); + break; + default: + printf("Dest Unreachable, Bad Code: %d\n", code); + break; + } + if (icp && (options & F_VERBOSE)) + pr_iph((struct iphdr*)(icp + 1)); + break; + case ICMP_SOURCE_QUENCH: + printf("Source Quench\n"); + if (icp && (options & F_VERBOSE)) + pr_iph((struct iphdr*)(icp + 1)); + break; + case ICMP_REDIRECT: + switch(code) { + case ICMP_REDIR_NET: + printf("Redirect Network"); + break; + case ICMP_REDIR_HOST: + printf("Redirect Host"); + break; + case ICMP_REDIR_NETTOS: + printf("Redirect Type of Service and Network"); + break; + case ICMP_REDIR_HOSTTOS: + printf("Redirect Type of Service and Host"); + break; + default: + printf("Redirect, Bad Code: %d", code); + break; + } + { + struct sockaddr_in sin = { .sin_family = AF_INET, .sin_addr = { icp ? icp->un.gateway : info } }; + + printf("(New nexthop: %s)\n", pr_addr(&sin, sizeof sin)); + } + if (icp && (options & F_VERBOSE)) + pr_iph((struct iphdr*)(icp + 1)); + break; + case ICMP_ECHO: + printf("Echo Request\n"); + /* XXX ID + Seq + Data */ + break; + case ICMP_TIME_EXCEEDED: + switch(code) { + case ICMP_EXC_TTL: + printf("Time to live exceeded\n"); + break; + case ICMP_EXC_FRAGTIME: + printf("Frag reassembly time exceeded\n"); + break; + default: + printf("Time exceeded, Bad Code: %d\n", code); + break; + } + if (icp && (options & F_VERBOSE)) + pr_iph((struct iphdr*)(icp + 1)); + break; + case ICMP_PARAMETERPROB: + printf("Parameter problem: pointer = %u\n", icp ? (ntohl(icp->un.gateway)>>24) : info); + if (icp && (options & F_VERBOSE)) + pr_iph((struct iphdr*)(icp + 1)); + break; + case ICMP_TIMESTAMP: + printf("Timestamp\n"); + /* XXX ID + Seq + 3 timestamps */ + break; + case ICMP_TIMESTAMPREPLY: + printf("Timestamp Reply\n"); + /* XXX ID + Seq + 3 timestamps */ + break; + case ICMP_INFO_REQUEST: + printf("Information Request\n"); + /* XXX ID + Seq */ + break; + case ICMP_INFO_REPLY: + printf("Information Reply\n"); + /* XXX ID + Seq */ + break; +#ifdef ICMP_MASKREQ + case ICMP_MASKREQ: + printf("Address Mask Request\n"); + break; +#endif +#ifdef ICMP_MASKREPLY + case ICMP_MASKREPLY: + printf("Address Mask Reply\n"); + break; +#endif + default: + printf("Bad ICMP type: %d\n", type); + } +} + +void pr_options(unsigned char * cp, int hlen) +{ + int i, j; + int optlen, totlen; + unsigned char * optptr; + static int old_rrlen; + static char old_rr[MAX_IPOPTLEN]; + + totlen = hlen-sizeof(struct iphdr); + optptr = cp; + + while (totlen > 0) { + if (*optptr == IPOPT_EOL) + break; + if (*optptr == IPOPT_NOP) { + totlen--; + optptr++; + printf("\nNOP"); + continue; + } + cp = optptr; + optlen = optptr[1]; + if (optlen < 2 || optlen > totlen) + break; + + switch (*cp) { + case IPOPT_SSRR: + case IPOPT_LSRR: + printf("\n%cSRR: ", *cp==IPOPT_SSRR ? 'S' : 'L'); + j = *++cp; + cp++; + if (j > IPOPT_MINOFF) { + for (;;) { + __u32 address; + memcpy(&address, cp, 4); + cp += 4; + if (address == 0) + printf("\t0.0.0.0"); + else { + struct sockaddr_in sin = { .sin_family = AF_INET, .sin_addr = { address } }; + + printf("\t%s", pr_addr(&sin, sizeof sin)); + } + j -= 4; + putchar('\n'); + if (j <= IPOPT_MINOFF) + break; + } + } + break; + case IPOPT_RR: + j = *++cp; /* get length */ + i = *++cp; /* and pointer */ + if (i > j) + i = j; + i -= IPOPT_MINOFF; + if (i <= 0) + break; + if (i == old_rrlen + && !memcmp(cp, old_rr, i) + && !(options & F_FLOOD)) { + printf("\t(same route)"); + break; + } + old_rrlen = i; + memcpy(old_rr, (char *)cp, i); + printf("\nRR: "); + cp++; + for (;;) { + __u32 address; + memcpy(&address, cp, 4); + cp += 4; + if (address == 0) + printf("\t0.0.0.0"); + else { + struct sockaddr_in sin = { .sin_family = AF_INET, .sin_addr = { address } }; + + printf("\t%s", pr_addr(&sin, sizeof sin)); + } + i -= 4; + putchar('\n'); + if (i <= 0) + break; + } + break; + case IPOPT_TS: + { + int stdtime = 0, nonstdtime = 0; + __u8 flags; + j = *++cp; /* get length */ + i = *++cp; /* and pointer */ + if (i > j) + i = j; + i -= 5; + if (i <= 0) + break; + flags = *++cp; + printf("\nTS: "); + cp++; + for (;;) { + long l; + + if ((flags&0xF) != IPOPT_TS_TSONLY) { + __u32 address; + memcpy(&address, cp, 4); + cp += 4; + if (address == 0) + printf("\t0.0.0.0"); + else { + struct sockaddr_in sin = { .sin_family = AF_INET, .sin_addr = { address } }; + + printf("\t%s", pr_addr(&sin, sizeof sin)); + } + i -= 4; + if (i <= 0) + break; + } + l = *cp++; + l = (l<<8) + *cp++; + l = (l<<8) + *cp++; + l = (l<<8) + *cp++; + + if (l & 0x80000000) { + if (nonstdtime==0) + printf("\t%ld absolute not-standard", l&0x7fffffff); + else + printf("\t%ld not-standard", (l&0x7fffffff) - nonstdtime); + nonstdtime = l&0x7fffffff; + } else { + if (stdtime==0) + printf("\t%ld absolute", l); + else + printf("\t%ld", l - stdtime); + stdtime = l; + } + i -= 4; + putchar('\n'); + if (i <= 0) + break; + } + if (flags>>4) + printf("Unrecorded hops: %d\n", flags>>4); + break; + } + default: + printf("\nunknown option %x", *cp); + break; + } + totlen -= optlen; + optptr += optlen; + } +} + + +/* + * pr_iph -- + * Print an IP header with options. + */ +void pr_iph(struct iphdr *ip) +{ + int hlen; + unsigned char *cp; + + hlen = ip->ihl << 2; + cp = (unsigned char *)ip + 20; /* point to options */ + + printf("Vr HL TOS Len ID Flg off TTL Pro cks Src Dst Data\n"); + printf(" %1x %1x %02x %04x %04x", + ip->version, ip->ihl, ip->tos, ip->tot_len, ip->id); + printf(" %1x %04x", ((ip->frag_off) & 0xe000) >> 13, + (ip->frag_off) & 0x1fff); + printf(" %02x %02x %04x", ip->ttl, ip->protocol, ip->check); + printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->saddr)); + printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->daddr)); + printf("\n"); + pr_options(cp, hlen); +} + +/* + * pr_addr -- + * + * Return an ascii host address optionally with a hostname. + */ +char * +pr_addr(void *sa, socklen_t salen) +{ + static char buffer[4096] = ""; + static struct sockaddr_storage last_sa = { 0 }; + static socklen_t last_salen = 0; + char name[NI_MAXHOST] = ""; + char address[NI_MAXHOST] = ""; + + if (salen == last_salen && !memcmp(sa, &last_sa, salen)) + return buffer; + + memcpy(&last_sa, sa, (last_salen = salen)); + + in_pr_addr = !setjmp(pr_addr_jmp); + + getnameinfo(sa, salen, address, sizeof address, NULL, 0, getnameinfo_flags | NI_NUMERICHOST); + if (!exiting && !(options & F_NUMERIC)) + getnameinfo(sa, salen, name, sizeof name, NULL, 0, getnameinfo_flags); + + if (*name) + snprintf(buffer, sizeof buffer, "%s (%s)", name, address); + else + snprintf(buffer, sizeof buffer, "%s", address); + + in_pr_addr = 0; + + return(buffer); +} + + +/* Set Type of Service (TOS) and other Quality of Service relating bits */ +int parsetos(char *str) +{ + const char *cp; + int tos; + char *ep; + + /* handle both hex and decimal values */ + if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) { + cp = str + 2; + tos = (int)strtol(cp, &ep, 16); + } else + tos = (int)strtol(str, &ep, 10); + + /* doesn't look like decimal or hex, eh? */ + if (*ep != '\0') { + fprintf(stderr, "ping: \"%s\" bad value for TOS\n", str); + exit(2); + } + + if (tos > TOS_MAX) { + fprintf(stderr, "ping: the decimal value of TOS bits must be in range 0-255\n"); + exit(2); + } + return(tos); +} + +int parseflow(char *str) +{ + const char *cp; + unsigned long val; + char *ep; + + /* handle both hex and decimal values */ + if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) { + cp = str + 2; + val = (int)strtoul(cp, &ep, 16); + } else + val = (int)strtoul(str, &ep, 10); + + /* doesn't look like decimal or hex, eh? */ + if (*ep != '\0') { + fprintf(stderr, "ping: \"%s\" bad value for flowinfo\n", str); + exit(2); + } + + if (val & ~IPV6_FLOWINFO_FLOWLABEL) { /* Flow is 20 bit value */ + fprintf(stderr, "ping: \"%s\" value is greater than 20 bits.\n", str); + exit(2); + } + return(val); +} + + + +void ping4_install_filter(socket_st *sock) +{ + static int once; + static struct sock_filter insns[] = { + BPF_STMT(BPF_LDX|BPF_B|BPF_MSH, 0), /* Skip IP header. F..g BSD... Look into ping6. */ + BPF_STMT(BPF_LD|BPF_H|BPF_IND, 4), /* Load icmp echo ident */ + BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0xAAAA, 0, 1), /* Ours? */ + BPF_STMT(BPF_RET|BPF_K, ~0U), /* Yes, it passes. */ + BPF_STMT(BPF_LD|BPF_B|BPF_IND, 0), /* Load icmp type */ + BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, ICMP_ECHOREPLY, 1, 0), /* Echo? */ + BPF_STMT(BPF_RET|BPF_K, 0xFFFFFFF), /* No. It passes. */ + BPF_STMT(BPF_RET|BPF_K, 0) /* Echo with wrong ident. Reject. */ + }; + static struct sock_fprog filter = { + sizeof insns / sizeof(insns[0]), + insns + }; + + if (once) + return; + once = 1; + + /* Patch bpflet for current identifier. */ + insns[2] = (struct sock_filter)BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htons(ident), 0, 1); + + if (setsockopt(sock->fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter))) + perror("WARNING: failed to install socket filter\n"); +} + +#define USAGE_NEWLINE "\n " + +void usage(void) +{ + fprintf(stderr, + "Usage: ping" + " [-" + "aAbBdDfhLnOqrRUvV64" + "]" + " [-c count]" + " [-i interval]" + " [-I interface]" + USAGE_NEWLINE + " [-m mark]" + " [-M pmtudisc_option]" + " [-l preload]" + " [-p pattern]" + " [-Q tos]" + USAGE_NEWLINE + " [-s packetsize]" + " [-S sndbuf]" + " [-t ttl]" + " [-T timestamp_option]" + USAGE_NEWLINE + " [-w deadline]" + " [-W timeout]" + " [hop1 ...] destination" + "\n" + ); + ping6_usage(1); + exit(2); +} diff --git a/ping.h b/ping.h new file mode 100644 index 0000000..3e09685 --- /dev/null +++ b/ping.h @@ -0,0 +1,394 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CAPABILITIES +#include +#include +#endif + +#ifdef USE_IDN +#include +#define getaddrinfo_flags (AI_CANONNAME | AI_IDN | AI_CANONIDN) +#define getnameinfo_flags NI_IDN +#else +#define getaddrinfo_flags (AI_CANONNAME) +#define getnameinfo_flags 0 +#endif + +#ifndef WITHOUT_IFADDRS +#include +#endif + +#include +#include +#include +#include + +#include "in6_flowlabel.h" +#include "SNAPSHOT.h" + +#ifndef SCOPE_DELIMITER +#define SCOPE_DELIMITER '%' +#endif + +#define DEFDATALEN (64 - 8) /* default data length */ + +#define MAXWAIT 10 /* max seconds to wait for response */ +#define MININTERVAL 10 /* Minimal interpacket gap */ +#define MINUSERINTERVAL 200 /* Minimal allowed interval for non-root */ + +#define SCHINT(a) (((a) <= MININTERVAL) ? MININTERVAL : (a)) + +/* various options */ +extern int options; +#define F_FLOOD 0x001 +#define F_INTERVAL 0x002 +#define F_NUMERIC 0x004 +#define F_PINGFILLED 0x008 +#define F_QUIET 0x010 +#define F_RROUTE 0x020 +#define F_SO_DEBUG 0x040 +#define F_SO_DONTROUTE 0x080 +#define F_VERBOSE 0x100 +#define F_TIMESTAMP 0x200 +#define F_SOURCEROUTE 0x400 +#define F_FLOOD_POLL 0x800 +#define F_LATENCY 0x1000 +#define F_AUDIBLE 0x2000 +#define F_ADAPTIVE 0x4000 +#define F_STRICTSOURCE 0x8000 +#define F_NOLOOP 0x10000 +#define F_TTL 0x20000 +#define F_MARK 0x40000 +#define F_PTIMEOFDAY 0x80000 +#define F_OUTSTANDING 0x100000 +#define F_FLOWINFO 0x200000 +#define F_TCLASS 0x400000 + +/* + * MAX_DUP_CHK is the number of bits in received table, i.e. the maximum + * number of received sequence numbers we can keep track of. + */ +#define MAX_DUP_CHK 0x10000 + +#if defined(__WORDSIZE) && __WORDSIZE == 64 +# define USE_BITMAP64 +#endif + +#ifdef USE_BITMAP64 +typedef __u64 bitmap_t; +# define BITMAP_SHIFT 6 +#else +typedef __u32 bitmap_t; +# define BITMAP_SHIFT 5 +#endif + +#if ((MAX_DUP_CHK >> (BITMAP_SHIFT + 3)) << (BITMAP_SHIFT + 3)) != MAX_DUP_CHK +# error Please MAX_DUP_CHK and/or BITMAP_SHIFT +#endif + +struct rcvd_table { + bitmap_t bitmap[MAX_DUP_CHK / (sizeof(bitmap_t) * 8)]; +}; + +extern struct rcvd_table rcvd_tbl; + +#define A(bit) (rcvd_tbl.bitmap[(bit) >> BITMAP_SHIFT]) /* identify word in array */ +#define B(bit) (((bitmap_t)1) << ((bit) & ((1 << BITMAP_SHIFT) - 1))) /* identify bit in word */ + +static inline void rcvd_set(__u16 seq) +{ + unsigned bit = seq % MAX_DUP_CHK; + A(bit) |= B(bit); +} + +static inline void rcvd_clear(__u16 seq) +{ + unsigned bit = seq % MAX_DUP_CHK; + A(bit) &= ~B(bit); +} + +static inline bitmap_t rcvd_test(__u16 seq) +{ + unsigned bit = seq % MAX_DUP_CHK; + return A(bit) & B(bit); +} + +extern int datalen; +extern char *hostname; +extern int uid; +extern int ident; /* process id to identify our packets */ + +extern int sndbuf; +extern int ttl; + +extern long npackets; /* max packets to transmit */ +extern long nreceived; /* # of packets we got back */ +extern long nrepeats; /* number of duplicates */ +extern long ntransmitted; /* sequence # for outbound packets = #sent */ +extern long nchecksum; /* replies with bad checksum */ +extern long nerrors; /* icmp errors */ +extern int interval; /* interval between packets (msec) */ +extern int preload; +extern int deadline; /* time to die */ +extern int lingertime; +extern struct timeval start_time, cur_time; +extern volatile int exiting; +extern volatile int status_snapshot; +extern int confirm; +extern int confirm_flag; +extern char *device; +extern int pmtudisc; + +extern volatile int in_pr_addr; /* pr_addr() is executing */ +extern jmp_buf pr_addr_jmp; + +#ifndef MSG_CONFIRM +#define MSG_CONFIRM 0 +#endif + + +/* timing */ +extern int timing; /* flag to do timing */ +extern long tmin; /* minimum round trip time */ +extern long tmax; /* maximum round trip time */ +extern long long tsum; /* sum of all times, for doing average */ +extern long long tsum2; +extern int rtt; +extern __u16 acked; +extern int pipesize; + +/* + * Write to stdout + */ +static inline void write_stdout(const char *str, size_t len) +{ + size_t o = 0; + ssize_t cc; + do { + cc = write(STDOUT_FILENO, str + o, len - o); + o += cc; + } while (len > o || cc < 0); +} + +/* + * tvsub -- + * Subtract 2 timeval structs: out = out - in. Out is assumed to + * be >= in. + */ +static inline void tvsub(struct timeval *out, struct timeval *in) +{ + if ((out->tv_usec -= in->tv_usec) < 0) { + --out->tv_sec; + out->tv_usec += 1000000; + } + out->tv_sec -= in->tv_sec; +} + +static inline void set_signal(int signo, void (*handler)(int)) +{ + struct sigaction sa; + + memset(&sa, 0, sizeof(sa)); + + sa.sa_handler = (void (*)(int))handler; +#ifdef SA_INTERRUPT + sa.sa_flags = SA_INTERRUPT; +#endif + sigaction(signo, &sa, NULL); +} + +extern int __schedule_exit(int next); + +static inline int schedule_exit(int next) +{ + if (npackets && ntransmitted >= npackets && !deadline) + next = __schedule_exit(next); + return next; +} + +static inline int in_flight(void) +{ + __u16 diff = (__u16)ntransmitted - acked; + return (diff<=0x7FFF) ? diff : ntransmitted-nreceived-nerrors; +} + +static inline void acknowledge(__u16 seq) +{ + __u16 diff = (__u16)ntransmitted - seq; + if (diff <= 0x7FFF) { + if ((int)diff+1 > pipesize) + pipesize = (int)diff+1; + if ((__s16)(seq - acked) > 0 || + (__u16)ntransmitted - acked > 0x7FFF) + acked = seq; + } +} + +static inline void advance_ntransmitted(void) +{ + ntransmitted++; + /* Invalidate acked, if 16 bit seq overflows. */ + if ((__u16)ntransmitted - acked > 0x7FFF) + acked = (__u16)ntransmitted + 1; +} + +extern void limit_capabilities(void); +static int enable_capability_raw(void); +static int disable_capability_raw(void); +static int enable_capability_admin(void); +static int disable_capability_admin(void); +#ifdef CAPABILITIES +extern int modify_capability(cap_value_t, cap_flag_value_t); +static inline int enable_capability_raw(void) { return modify_capability(CAP_NET_RAW, CAP_SET); }; +static inline int disable_capability_raw(void) { return modify_capability(CAP_NET_RAW, CAP_CLEAR); }; +static inline int enable_capability_admin(void) { return modify_capability(CAP_NET_ADMIN, CAP_SET); }; +static inline int disable_capability_admin(void) { return modify_capability(CAP_NET_ADMIN, CAP_CLEAR); }; +#else +extern int modify_capability(int); +static inline int enable_capability_raw(void) { return modify_capability(1); }; +static inline int disable_capability_raw(void) { return modify_capability(0); }; +static inline int enable_capability_admin(void) { return modify_capability(1); }; +static inline int disable_capability_admin(void) { return modify_capability(0); }; +#endif +extern void drop_capabilities(void); + +typedef struct socket_st { + int fd; + int socktype; +} socket_st; + +char *pr_addr(void *sa, socklen_t salen); + +int is_ours(socket_st *sock, uint16_t id); + +int ping4_run(int argc, char **argv, struct addrinfo *ai, socket_st *sock); +int ping4_send_probe(socket_st *, void *packet, unsigned packet_size); +int ping4_receive_error_msg(socket_st *); +int ping4_parse_reply(socket_st *, struct msghdr *msg, int len, void *addr, struct timeval *); +void ping4_install_filter(socket_st *); + +typedef struct ping_func_set_st { + int (*send_probe)(socket_st *, void *packet, unsigned packet_size); + int (*receive_error_msg)(socket_st *sock); + int (*parse_reply)(socket_st *, struct msghdr *msg, int len, void *addr, struct timeval *); + void (*install_filter)(socket_st *); +} ping_func_set_st; + +#define MAXPACKET 128000 /* max packet size */ +extern ping_func_set_st ping4_func_set; + +extern int pinger(ping_func_set_st *fset, socket_st *sock); +extern void sock_setbufs(socket_st*, int alloc); +extern void setup(socket_st *); +extern int contains_pattern_in_payload(__u8 *ptr); +extern void main_loop(ping_func_set_st *fset, socket_st*, __u8 *buf, int buflen) __attribute__((noreturn)); +extern void finish(void) __attribute__((noreturn)); +extern void status(void); +extern void common_options(int ch); +extern int gather_statistics(__u8 *ptr, int icmplen, + int cc, __u16 seq, int hops, + int csfailed, struct timeval *tv, char *from, + void (*pr_reply)(__u8 *ptr, int cc)); +extern void print_timestamp(void); +void fill(char *patp, void *packet, unsigned packet_size); + +extern int mark; +extern unsigned char outpack[MAXPACKET]; + +/* IPv6 */ + +int ping6_run(int argc, char **argv, struct addrinfo *ai, socket_st *sock); +void ping6_usage(unsigned from_ping); + +int ping6_send_probe(socket_st *sockets, void *packet, unsigned packet_size); +int ping6_receive_error_msg(socket_st *sockets); +int ping6_parse_reply(socket_st *, struct msghdr *msg, int len, void *addr, struct timeval *); +void ping6_install_filter(socket_st *sockets); + +extern ping_func_set_st ping6_func_set; + +int niquery_option_handler(const char *opt_arg); + +extern __u32 tclass; +extern __u32 flowlabel; +extern struct sockaddr_in6 source6; +extern struct sockaddr_in6 whereto6; +extern struct sockaddr_in6 firsthop6; + +/* IPv6 node information query */ + +#define NI_NONCE_SIZE 8 + +struct ni_hdr { + struct icmp6_hdr ni_u; + __u8 ni_nonce[NI_NONCE_SIZE]; +}; + +#define ni_type ni_u.icmp6_type +#define ni_code ni_u.icmp6_code +#define ni_cksum ni_u.icmp6_cksum +#define ni_qtype ni_u.icmp6_data16[0] +#define ni_flags ni_u.icmp6_data16[1] + +/* Types */ +#ifndef ICMPV6_NI_QUERY +# define ICMPV6_NI_QUERY 139 +# define ICMPV6_NI_REPLY 140 +#endif + +/* Query Codes */ +#define NI_SUBJ_IPV6 0 +#define NI_SUBJ_NAME 1 +#define NI_SUBJ_IPV4 2 + +/* Reply Codes */ +#define NI_SUCCESS 0 +#define NI_REFUSED 1 +#define NI_UNKNOWN 2 + +/* Qtypes */ +#define NI_QTYPE_NOOP 0 +#define NI_QTYPE_NAME 2 +#define NI_QTYPE_IPV6ADDR 3 +#define NI_QTYPE_IPV4ADDR 4 + +/* Flags */ +#define NI_IPV6ADDR_F_TRUNCATE __constant_cpu_to_be16(0x0001) +#define NI_IPV6ADDR_F_ALL __constant_cpu_to_be16(0x0002) +#define NI_IPV6ADDR_F_COMPAT __constant_cpu_to_be16(0x0004) +#define NI_IPV6ADDR_F_LINKLOCAL __constant_cpu_to_be16(0x0008) +#define NI_IPV6ADDR_F_SITELOCAL __constant_cpu_to_be16(0x0010) +#define NI_IPV6ADDR_F_GLOBAL __constant_cpu_to_be16(0x0020) + +#define NI_IPV4ADDR_F_TRUNCATE NI_IPV6ADDR_F_TRUNCATE +#define NI_IPV4ADDR_F_ALL NI_IPV6ADDR_F_ALL diff --git a/ping6_common.c b/ping6_common.c new file mode 100644 index 0000000..5991c2a --- /dev/null +++ b/ping6_common.c @@ -0,0 +1,1612 @@ +/* + * + * Modified for AF_INET6 by Pedro Roque + * + * + * + * Original copyright notice included bellow + */ + +/* + * Copyright (c) 1989 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Mike Muuss. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * P I N G . C + * + * Using the InterNet Control Message Protocol (ICMP) "ECHO" facility, + * measure round-trip-delays and packet loss across network paths. + * + * Author - + * Mike Muuss + * U. S. Army Ballistic Research Laboratory + * December, 1983 + * + * Status - + * Public Domain. Distribution Unlimited. + * Bugs - + * More statistics could always be gathered. + * If kernel does not support non-raw ICMP sockets or + * if -N option is used, this program has to run SUID to ROOT or + * with net_cap_raw enabled. + */ +#include "ping.h" + +#if defined(ENABLE_PING6_RTHDR) && !defined(ENABLE_PING6_RTHDR_RFC3542) +#ifndef IPV6_SRCRT_TYPE_0 +#define IPV6_SRCRT_TYPE_0 0 +#endif +#endif + +ping_func_set_st ping6_func_set = { + .send_probe = ping6_send_probe, + .receive_error_msg = ping6_receive_error_msg, + .parse_reply = ping6_parse_reply, + .install_filter = ping6_install_filter +}; + +#define BIT_CLEAR(nr, addr) do { ((__u32 *)(addr))[(nr) >> 5] &= ~(1U << ((nr) & 31)); } while(0) +#define BIT_SET(nr, addr) do { ((__u32 *)(addr))[(nr) >> 5] |= (1U << ((nr) & 31)); } while(0) +#define BIT_TEST(nr, addr) do { (__u32 *)(addr))[(nr) >> 5] & (1U << ((nr) & 31)); } while(0) + +#ifndef SCOPE_DELIMITER +# define SCOPE_DELIMITER '%' +#endif + +__u32 flowlabel; +__u32 tclass; +#ifdef ENABLE_PING6_RTHDR +struct cmsghdr *srcrt; +#endif + +static struct sockaddr_in6 whereto; +static struct sockaddr_in6 firsthop; + +static unsigned char cmsgbuf[4096]; +static int cmsglen = 0; + +static int pr_icmph(__u8 type, __u8 code, __u32 info); +void ping6_usage(unsigned) __attribute((noreturn)); + +struct sockaddr_in6 source6 = { .sin6_family = AF_INET6 }; +char *device; + +#if defined(USE_GCRYPT) || defined(USE_OPENSSL) || defined(USE_NETTLE) +#include "iputils_md5dig.h" +#define USE_CRYPTO +#endif + +/* Node Information query */ +int ni_query = -1; +int ni_flag = 0; +void *ni_subject = NULL; +int ni_subject_len = 0; +int ni_subject_type = -1; +char *ni_group; + +static inline int ntohsp(__u16 *p) +{ + __u16 v; + memcpy(&v, p, sizeof(v)); + return ntohs(v); +} + +#if defined(ENABLE_PING6_RTHDR) && !defined(ENABLE_PING6_RTHDR_RFC3542) +size_t inet6_srcrt_space(int type, int segments) +{ + if (type != 0 || segments > 24) + return 0; + + return (sizeof(struct cmsghdr) + sizeof(struct ip6_rthdr0) + + segments * sizeof(struct in6_addr)); +} + +extern struct cmsghdr * inet6_srcrt_init(void *bp, int type) +{ + struct cmsghdr *cmsg; + + if (type) + return NULL; + + memset(bp, 0, sizeof(struct cmsghdr) + sizeof(struct ip6_rthdr0)); + cmsg = (struct cmsghdr *) bp; + + cmsg->cmsg_len = sizeof(struct cmsghdr) + sizeof(struct ip6_rthdr0); + cmsg->cmsg_level = IPPROTO_IPV6; + cmsg->cmsg_type = IPV6_RTHDR; + + return cmsg; +} + +int inet6_srcrt_add(struct cmsghdr *cmsg, const struct in6_addr *addr) +{ + struct ip6_rthdr0 *hdr; + + hdr = (struct ip6_rthdr0 *) CMSG_DATA(cmsg); + + cmsg->cmsg_len += sizeof(struct in6_addr); + hdr->ip6r0_len += sizeof(struct in6_addr) / 8; + + memcpy(&hdr->ip6r0_addr[hdr->ip6r0_segleft++], addr, + sizeof(struct in6_addr)); + + return 0; +} +#endif + +unsigned int if_name2index(const char *ifname) +{ + unsigned int i = if_nametoindex(ifname); + if (!i) { + fprintf(stderr, "ping: unknown iface %s\n", ifname); + exit(2); + } + return i; +} + +struct niquery_option { + char *name; + int namelen; + int has_arg; + int data; + int (*handler)(int index, const char *arg); +}; + +#define NIQUERY_OPTION(_name, _has_arg, _data, _handler) \ + { \ + .name = _name, \ + .namelen = sizeof(_name) - 1, \ + .has_arg = _has_arg, \ + .data = _data, \ + .handler = _handler \ + } + +static int niquery_option_name_handler(int index, const char *arg); +static int niquery_option_ipv6_handler(int index, const char *arg); +static int niquery_option_ipv6_flag_handler(int index, const char *arg); +static int niquery_option_ipv4_handler(int index, const char *arg); +static int niquery_option_ipv4_flag_handler(int index, const char *arg); +static int niquery_option_subject_addr_handler(int index, const char *arg); +static int niquery_option_subject_name_handler(int index, const char *arg); +static int niquery_option_help_handler(int index, const char *arg); + +struct niquery_option niquery_options[] = { + NIQUERY_OPTION("name", 0, 0, niquery_option_name_handler), + NIQUERY_OPTION("fqdn", 0, 0, niquery_option_name_handler), + NIQUERY_OPTION("ipv6", 0, 0, niquery_option_ipv6_handler), + NIQUERY_OPTION("ipv6-all", 0, NI_IPV6ADDR_F_ALL, niquery_option_ipv6_flag_handler), + NIQUERY_OPTION("ipv6-compatible", 0, NI_IPV6ADDR_F_COMPAT, niquery_option_ipv6_flag_handler), + NIQUERY_OPTION("ipv6-linklocal", 0, NI_IPV6ADDR_F_LINKLOCAL, niquery_option_ipv6_flag_handler), + NIQUERY_OPTION("ipv6-sitelocal", 0, NI_IPV6ADDR_F_SITELOCAL, niquery_option_ipv6_flag_handler), + NIQUERY_OPTION("ipv6-global", 0, NI_IPV6ADDR_F_GLOBAL, niquery_option_ipv6_flag_handler), + NIQUERY_OPTION("ipv4", 0, 0, niquery_option_ipv4_handler), + NIQUERY_OPTION("ipv4-all", 0, NI_IPV4ADDR_F_ALL, niquery_option_ipv4_flag_handler), + NIQUERY_OPTION("subject-ipv6", 1, NI_SUBJ_IPV6, niquery_option_subject_addr_handler), + NIQUERY_OPTION("subject-ipv4", 1, NI_SUBJ_IPV4, niquery_option_subject_addr_handler), + NIQUERY_OPTION("subject-name", 1, 0, niquery_option_subject_name_handler), + NIQUERY_OPTION("subject-fqdn", 1, -1, niquery_option_subject_name_handler), + NIQUERY_OPTION("help", 0, 0, niquery_option_help_handler), + {}, +}; + +static inline int niquery_is_enabled(void) +{ + return ni_query >= 0; +} + +#if PING6_NONCE_MEMORY +__u8 *ni_nonce_ptr; +#else +struct { + struct timeval tv; + pid_t pid; +} ni_nonce_secret; +#endif + +static void niquery_init_nonce(void) +{ +#if PING6_NONCE_MEMORY + struct timeval tv; + unsigned long seed; + + seed = (unsigned long)getpid(); + if (!gettimeofday(&tv, NULL)) + seed ^= tv.tv_usec; + srand(seed); + + ni_nonce_ptr = calloc(NI_NONCE_SIZE, MAX_DUP_CHK); + if (!ni_nonce_ptr) { + perror("ping6: calloc"); + exit(2); + } + + ni_nonce_ptr[0] = ~0; +#else + gettimeofday(&ni_nonce_secret.tv, NULL); + ni_nonce_secret.pid = getpid(); +#endif +} + +#if !PING6_NONCE_MEMORY +static int niquery_nonce(__u8 *nonce, int fill) +{ +# ifdef USE_CRYPTO + static __u8 digest[MD5_DIGEST_LENGTH]; + static int seq = -1; + + if (fill || seq != *(__u16 *)nonce || seq < 0) { + MD5_CTX ctxt; + + MD5_Init(&ctxt); + MD5_Update(&ctxt, &ni_nonce_secret, sizeof(ni_nonce_secret)); + MD5_Update(&ctxt, nonce, sizeof(__u16)); + MD5_Final(digest, &ctxt); + + seq = *(__u16 *)nonce; + } + + if (fill) { + memcpy(nonce + sizeof(__u16), digest, NI_NONCE_SIZE - sizeof(__u16)); + return 0; + } else { + if (memcmp(nonce + sizeof(__u16), digest, NI_NONCE_SIZE - sizeof(__u16))) + return -1; + return ntohsp((__u16 *)nonce); + } +# else + fprintf(stderr, "ping6: function not available; crypto disabled\n"); + exit(3); +# endif +} +#endif + +static inline void niquery_fill_nonce(__u16 seq, __u8 *nonce) +{ + __u16 v = htons(seq); +#if PING6_NONCE_MEMORY + int i; + + memcpy(&ni_nonce_ptr[NI_NONCE_SIZE * (seq % MAX_DUP_CHK)], &v, sizeof(v)); + + for (i = sizeof(v); i < NI_NONCE_SIZE; i++) + ni_nonce_ptr[NI_NONCE_SIZE * (seq % MAX_DUP_CHK) + i] = 0x100 * (rand() / (RAND_MAX + 1.0)); + + memcpy(nonce, &ni_nonce_ptr[NI_NONCE_SIZE * (seq % MAX_DUP_CHK)], NI_NONCE_SIZE); +#else + memcpy(nonce, &v, sizeof(v)); + niquery_nonce(nonce, 1); +#endif +} + +static inline int niquery_check_nonce(__u8 *nonce) +{ +#if PING6_NONCE_MEMORY + __u16 seq = ntohsp((__u16 *)nonce); + if (memcmp(nonce, &ni_nonce_ptr[NI_NONCE_SIZE * (seq % MAX_DUP_CHK)], NI_NONCE_SIZE)) + return -1; + return seq; +#else + return niquery_nonce(nonce, 0); +#endif +} + +static int niquery_set_qtype(int type) +{ + if (niquery_is_enabled() && ni_query != type) { + printf("Qtype conflict\n"); + return -1; + } + ni_query = type; + return 0; +} + +static int niquery_option_name_handler(int index, const char *arg) +{ + if (niquery_set_qtype(NI_QTYPE_NAME) < 0) + return -1; + return 0; +} + +static int niquery_option_ipv6_handler(int index, const char *arg) +{ + if (niquery_set_qtype(NI_QTYPE_IPV6ADDR) < 0) + return -1; + return 0; +} + +static int niquery_option_ipv6_flag_handler(int index, const char *arg) +{ + if (niquery_set_qtype(NI_QTYPE_IPV6ADDR) < 0) + return -1; + ni_flag |= niquery_options[index].data; + return 0; +} + +static int niquery_option_ipv4_handler(int index, const char *arg) +{ + if (niquery_set_qtype(NI_QTYPE_IPV4ADDR) < 0) + return -1; + return 0; +} + +static int niquery_option_ipv4_flag_handler(int index, const char *arg) +{ + if (niquery_set_qtype(NI_QTYPE_IPV4ADDR) < 0) + return -1; + ni_flag |= niquery_options[index].data; + return 0; +} + +static inline int niquery_is_subject_valid(void) +{ + return ni_subject_type >= 0 && ni_subject; +} + +static int niquery_set_subject_type(int type) +{ + if (niquery_is_subject_valid() && ni_subject_type != type) { + printf("Subject type conflict\n"); + return -1; + } + ni_subject_type = type; + return 0; +} + +#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])) +#define OFFSET_OF(type,elem) ((size_t)&((type *)0)->elem) + +static int niquery_option_subject_addr_handler(int index, const char *arg) +{ + struct addrinfo hints = { .ai_family = AF_UNSPEC, .ai_socktype = SOCK_DGRAM, .ai_flags = getaddrinfo_flags }; + struct addrinfo *result, *ai; + int status; + int offset; + + if (niquery_set_subject_type(niquery_options[index].data) < 0) + return -1; + + ni_subject_type = niquery_options[index].data; + + switch (niquery_options[index].data) { + case NI_SUBJ_IPV6: + ni_subject_len = sizeof(struct in6_addr); + offset = OFFSET_OF(struct sockaddr_in6, sin6_addr); + hints.ai_family = AF_INET6; + break; + case NI_SUBJ_IPV4: + ni_subject_len = sizeof(struct in_addr); + offset = OFFSET_OF(struct sockaddr_in, sin_addr); + hints.ai_family = AF_INET; + break; + default: + /* should not happen. */ + offset = -1; + } + + status = getaddrinfo(arg, 0, &hints, &result); + if (status) { + fprintf(stderr, "ping6: %s: %s\n", arg, gai_strerror(status)); + return -1; + } + + for (ai = result; ai; ai = ai->ai_next) { + void *p = malloc(ni_subject_len); + if (!p) + continue; + memcpy(p, (__u8 *)ai->ai_addr + offset, ni_subject_len); + free(ni_subject); + ni_subject = p; + break; + } + freeaddrinfo(result); + + return 0; +} + +#ifdef USE_IDN +# if IDN2_VERSION_NUMBER >= 0x02000000 +# define IDN2_FLAGS IDN2_NONTRANSITIONAL +# else +# define IDN2_FLAGS 0 +# endif +#endif + +static int niquery_option_subject_name_handler(int index, const char *name) +{ +#ifdef USE_CRYPTO + static char nigroup_buf[INET6_ADDRSTRLEN + 1 + IFNAMSIZ]; + unsigned char *dnptrs[2], **dpp, **lastdnptr; + int n; + int i; + char *p; + char *canonname = NULL, *idn = NULL; + unsigned char *buf = NULL; + size_t namelen; + size_t buflen; + int dots, fqdn = niquery_options[index].data; + MD5_CTX ctxt; + __u8 digest[MD5_DIGEST_LENGTH]; +#ifdef USE_IDN + int rc; +#endif + + if (niquery_set_subject_type(NI_SUBJ_NAME) < 0) + return -1; + +#ifdef USE_IDN + rc = idn2_lookup_ul(name, &idn, IDN2_FLAGS); + if (rc) { + fprintf(stderr, "ping6: IDN encoding error: %s\n", + idn2_strerror(rc)); + exit(2); + } +#else + idn = strdup(name); + if (!idn) + goto oomexit; +#endif + + p = strchr(idn, SCOPE_DELIMITER); + if (p) { + *p = '\0'; + if (strlen(p + 1) >= IFNAMSIZ) { + fprintf(stderr, "ping6: too long scope name.\n"); + exit(1); + } + } + + namelen = strlen(idn); + canonname = malloc(namelen + 1); + if (!canonname) + goto oomexit; + + dots = 0; + for (i = 0; i < namelen + 1; i++) { + canonname[i] = isupper(idn[i]) ? tolower(idn[i]) : idn[i]; + if (idn[i] == '.') + dots++; + } + + if (fqdn == 0) { + /* guess if hostname is FQDN */ + fqdn = dots ? 1 : -1; + } + + buflen = namelen + 3 + 1; /* dn_comp() requrires strlen() + 3, + plus non-fqdn indicator. */ + buf = malloc(buflen); + if (!buf) { + fprintf(stderr, "ping6: out of memory.\n"); + goto errexit; + } + + dpp = dnptrs; + lastdnptr = &dnptrs[ARRAY_SIZE(dnptrs)]; + + *dpp++ = (unsigned char *)buf; + *dpp++ = NULL; + + n = dn_comp(canonname, (unsigned char *)buf, buflen, dnptrs, lastdnptr); + if (n < 0) { + fprintf(stderr, "ping6: Inappropriate subject name: %s\n", canonname); + goto errexit; + } else if (n >= buflen) { + fprintf(stderr, "ping6: dn_comp() returned too long result.\n"); + goto errexit; + } + + MD5_Init(&ctxt); + MD5_Update(&ctxt, buf, buf[0]); + MD5_Final(digest, &ctxt); + + sprintf(nigroup_buf, "ff02::2:%02x%02x:%02x%02x%s%s", + digest[0], digest[1], digest[2], digest[3], + p ? "%" : "", + p ? p + 1 : ""); + + if (fqdn < 0) + buf[n] = 0; + + free(ni_subject); + + ni_group = nigroup_buf; + ni_subject = buf; + ni_subject_len = n + (fqdn < 0); + ni_group = nigroup_buf; + + free(canonname); + free(idn); + + return 0; +oomexit: + fprintf(stderr, "ping6: out of memory.\n"); +errexit: + free(buf); + free(canonname); + free(idn); + exit(1); +#else + fprintf(stderr, "ping6: function not available; crypto disabled\n"); + exit(3); +#endif +} + +int niquery_option_help_handler(int index, const char *arg) +{ + fprintf(stderr, "ping6 -N suboptions\n" + "\tHelp:\n" + "\t\thelp\n" + "\tQuery:\n" + "\t\tname,\n" + "\t\tipv6,ipv6-all,ipv6-compatible,ipv6-linklocal,ipv6-sitelocal,ipv6-global,\n" + "\t\tipv4,ipv4-all,\n" + "\tSubject:\n" + "\t\tsubject-ipv6=addr,subject-ipv4=addr,subject-name=name,subject-fqdn=name,\n" + ); + exit(2); +} + +int niquery_option_handler(const char *opt_arg) +{ + struct niquery_option *p; + int i; + int ret = -1; + for (i = 0, p = niquery_options; p->name; i++, p++) { + if (strncmp(p->name, opt_arg, p->namelen)) + continue; + if (!p->has_arg) { + if (opt_arg[p->namelen] == '\0') { + ret = p->handler(i, NULL); + if (ret >= 0) + break; + } + } else { + if (opt_arg[p->namelen] == '=') { + ret = p->handler(i, &opt_arg[p->namelen] + 1); + if (ret >= 0) + break; + } + } + } + if (!p->name) + ret = niquery_option_help_handler(0, NULL); + return ret; +} + +int ping6_run(int argc, char **argv, struct addrinfo *ai, struct socket_st *sock) +{ + static const struct addrinfo hints = { .ai_family = AF_INET6, .ai_flags = getaddrinfo_flags }; + struct addrinfo *result = NULL; + int status; + int hold, packlen; + unsigned char *packet; + char *target; + struct icmp6_filter filter; + int err; + static uint32_t scope_id = 0; + +#ifdef ENABLE_PING6_RTHDR + while (argc > 1) { + struct in6_addr *addr; + + if (srcrt == NULL) { + int space; + + fprintf(stderr, "ping6: Warning: " + "Source routing is deprecated by RFC5095.\n"); + +#ifdef ENABLE_PING6_RTHDR_RFC3542 + space = inet6_rth_space(IPV6_RTHDR_TYPE_0, argc - 1); +#else + space = inet6_srcrt_space(IPV6_SRCRT_TYPE_0, argc - 1); +#endif + if (space == 0) { + fprintf(stderr, "srcrt_space failed\n"); + exit(2); + } +#ifdef ENABLE_PING6_RTHDR_RFC3542 + if (cmsglen + CMSG_SPACE(space) > sizeof(cmsgbuf)) { + fprintf(stderr, "no room for options\n"); + exit(2); + } +#else + if (space + cmsglen > sizeof(cmsgbuf)) { + fprintf(stderr, "no room for options\n"); + exit(2); + } +#endif + srcrt = (struct cmsghdr*)(cmsgbuf+cmsglen); +#ifdef ENABLE_PING6_RTHDR_RFC3542 + memset(srcrt, 0, CMSG_SPACE(0)); + srcrt->cmsg_len = CMSG_LEN(space); + srcrt->cmsg_level = IPPROTO_IPV6; + srcrt->cmsg_type = IPV6_RTHDR; + inet6_rth_init(CMSG_DATA(srcrt), space, IPV6_RTHDR_TYPE_0, argc - 1); + cmsglen += CMSG_SPACE(space); +#else + cmsglen += CMSG_ALIGN(space); + inet6_srcrt_init(srcrt, IPV6_SRCRT_TYPE_0); +#endif + } + + target = *argv; + + status = getaddrinfo(target, NULL, &hints, &result); + if (status) { + fprintf(stderr, "ping6: %s: %s\n", target, gai_strerror(status)); + exit(2); + } + addr = &((struct sockaddr_in6 *)(result->ai_addr))->sin6_addr; +#ifdef ENABLE_PING6_RTHDR_RFC3542 + inet6_rth_add(CMSG_DATA(srcrt), addr); +#else + inet6_srcrt_add(srcrt, addr); +#endif + if (IN6_IS_ADDR_UNSPECIFIED(&firsthop.sin6_addr)) { + memcpy(&firsthop.sin6_addr, addr, 16); + firsthop.sin6_scope_id = ((struct sockaddr_in6 *)(result->ai_addr))->sin6_scope_id; + /* Verify scope_id is the same as previous nodes */ + if (firsthop.sin6_scope_id && scope_id && firsthop.sin6_scope_id != scope_id) { + fprintf(stderr, "scope discrepancy among the nodes\n"); + exit(2); + } else if (!scope_id) { + scope_id = firsthop.sin6_scope_id; + } + } + freeaddrinfo(result); + + argv++; + argc--; + } +#endif + + if (niquery_is_enabled()) { + niquery_init_nonce(); + + if (!niquery_is_subject_valid()) { + ni_subject = &whereto.sin6_addr; + ni_subject_len = sizeof(whereto.sin6_addr); + ni_subject_type = NI_SUBJ_IPV6; + } + } + + if (argc > 1) { +#ifndef ENABLE_PING6_RTHDR + fprintf(stderr, "ping6: Source routing is deprecated by RFC5095.\n"); +#endif + ping6_usage(0); + } else if (argc == 1) { + target = *argv; + } else { + if (ni_query < 0 && ni_subject_type != NI_SUBJ_NAME) + ping6_usage(0); + target = ni_group; + } + + if (!ai) { + status = getaddrinfo(target, NULL, &hints, &result); + if (status) { + fprintf(stderr, "ping6: %s: %s\n", target, gai_strerror(status)); + exit(2); + } + ai = result; + } + + memcpy(&whereto, ai->ai_addr, sizeof(whereto)); + whereto.sin6_port = htons(IPPROTO_ICMPV6); + + if (result) + freeaddrinfo(result); + + if (memchr(target, ':', strlen(target))) + options |= F_NUMERIC; + + if (IN6_IS_ADDR_UNSPECIFIED(&firsthop.sin6_addr)) { + memcpy(&firsthop.sin6_addr, &whereto.sin6_addr, 16); + firsthop.sin6_scope_id = whereto.sin6_scope_id; + /* Verify scope_id is the same as intermediate nodes */ + if (firsthop.sin6_scope_id && scope_id && firsthop.sin6_scope_id != scope_id) { + fprintf(stderr, "scope discrepancy among the nodes\n"); + exit(2); + } else if (!scope_id) { + scope_id = firsthop.sin6_scope_id; + } + } + + hostname = target; + + if (IN6_IS_ADDR_UNSPECIFIED(&source6.sin6_addr)) { + socklen_t alen; + int probe_fd = socket(AF_INET6, SOCK_DGRAM, 0); + + if (probe_fd < 0) { + perror("socket"); + exit(2); + } + if (device) { + unsigned int iface = if_name2index(device); +#ifdef IPV6_RECVPKTINFO + struct in6_pktinfo ipi; + + memset(&ipi, 0, sizeof(ipi)); + ipi.ipi6_ifindex = iface; +#endif + + if (IN6_IS_ADDR_LINKLOCAL(&firsthop.sin6_addr) || + IN6_IS_ADDR_MC_LINKLOCAL(&firsthop.sin6_addr)) + firsthop.sin6_scope_id = iface; + enable_capability_raw(); +#ifdef IPV6_RECVPKTINFO + if ( + setsockopt(probe_fd, IPPROTO_IPV6, IPV6_PKTINFO, &ipi, sizeof ipi) == -1 || + setsockopt(sock->fd, IPPROTO_IPV6, IPV6_PKTINFO, &ipi, sizeof ipi) == -1) { + perror("setsockopt(IPV6_PKTINFO)"); + exit(2); + } +#endif + if ( + setsockopt(probe_fd, SOL_SOCKET, SO_BINDTODEVICE, device, strlen(device)+1) == -1 || + setsockopt(sock->fd, SOL_SOCKET, SO_BINDTODEVICE, device, strlen(device)+1) == -1) { + perror("setsockopt(SO_BINDTODEVICE)"); + exit(2); + } + disable_capability_raw(); + } + + if (!IN6_IS_ADDR_LINKLOCAL(&firsthop.sin6_addr) && + !IN6_IS_ADDR_MC_LINKLOCAL(&firsthop.sin6_addr)) + firsthop.sin6_family = AF_INET6; + + firsthop.sin6_port = htons(1025); + if (connect(probe_fd, (struct sockaddr*)&firsthop, sizeof(firsthop)) == -1) { + perror("connect"); + exit(2); + } + alen = sizeof source6; + if (getsockname(probe_fd, (struct sockaddr *) &source6, &alen) == -1) { + perror("getsockname"); + exit(2); + } + source6.sin6_port = 0; + close(probe_fd); + +#ifndef WITHOUT_IFADDRS + if (device) { + struct ifaddrs *ifa0, *ifa; + + if (getifaddrs(&ifa0)) { + perror("getifaddrs"); + exit(2); + } + + for (ifa = ifa0; ifa; ifa = ifa->ifa_next) { + if (!ifa->ifa_addr || ifa->ifa_addr->sa_family != AF_INET6) + continue; + if (!strncmp(ifa->ifa_name, device, sizeof(device) - 1) && + IN6_ARE_ADDR_EQUAL(&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr, + &source6.sin6_addr)) + break; + } + if (!ifa) + fprintf(stderr, "ping6: Warning: source address might be selected on device other than %s.\n", device); + + freeifaddrs(ifa0); + } +#endif + } + else if (device && (IN6_IS_ADDR_LINKLOCAL(&source6.sin6_addr) || + IN6_IS_ADDR_MC_LINKLOCAL(&source6.sin6_addr))) + source6.sin6_scope_id = if_name2index(device); + + if (device) { + struct cmsghdr *cmsg; + struct in6_pktinfo *ipi; + + cmsg = (struct cmsghdr*)(cmsgbuf+cmsglen); + cmsglen += CMSG_SPACE(sizeof(*ipi)); + cmsg->cmsg_len = CMSG_LEN(sizeof(*ipi)); + cmsg->cmsg_level = IPPROTO_IPV6; + cmsg->cmsg_type = IPV6_PKTINFO; + + ipi = (struct in6_pktinfo*)CMSG_DATA(cmsg); + memset(ipi, 0, sizeof(*ipi)); + ipi->ipi6_ifindex = if_name2index(device); + } + + if ((whereto.sin6_addr.s6_addr16[0]&htons(0xff00)) == htons (0xff00)) { + if (uid) { + if (interval < 1000) { + fprintf(stderr, "ping: multicast ping with too short interval.\n"); + exit(2); + } + if (pmtudisc >= 0 && pmtudisc != IPV6_PMTUDISC_DO) { + fprintf(stderr, "ping: multicast ping does not fragment.\n"); + exit(2); + } + } + if (pmtudisc < 0) + pmtudisc = IPV6_PMTUDISC_DO; + } + + if (pmtudisc >= 0) { + if (setsockopt(sock->fd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &pmtudisc, sizeof pmtudisc) == -1) { + perror("ping: IPV6_MTU_DISCOVER"); + exit(2); + } + } + + if ((options&F_STRICTSOURCE) && + bind(sock->fd, (struct sockaddr *) &source6, sizeof source6) == -1) { + perror("ping: bind icmp socket"); + exit(2); + } + + if (datalen >= sizeof(struct timeval) && (ni_query < 0)) { + /* can we time transfer */ + timing = 1; + } + packlen = datalen + 8 + 4096 + 40 + 8; /* 4096 for rthdr */ + if (!(packet = (unsigned char *)malloc((unsigned int)packlen))) { + fprintf(stderr, "ping: out of memory.\n"); + exit(2); + } + + hold = 1; + + /* Estimate memory eaten by single packet. It is rough estimate. + * Actually, for small datalen's it depends on kernel side a lot. */ + hold = datalen+8; + hold += ((hold+511)/512)*(40+16+64+160); + sock_setbufs(sock, hold); + +#ifdef __linux__ + if (sock->socktype == SOCK_RAW) { + int csum_offset = 2; + int sz_opt = sizeof(int); + + err = setsockopt(sock->fd, SOL_RAW, IPV6_CHECKSUM, &csum_offset, sz_opt); + if (err < 0) { + /* checksum should be enabled by default and setting this + * option might fail anyway. + */ + fprintf(stderr, "setsockopt(RAW_CHECKSUM) failed - try to continue."); + } +#endif + + /* + * select icmp echo reply as icmp type to receive + */ + + ICMP6_FILTER_SETBLOCKALL(&filter); + + ICMP6_FILTER_SETPASS(ICMP6_DST_UNREACH, &filter); + ICMP6_FILTER_SETPASS(ICMP6_PACKET_TOO_BIG, &filter); + ICMP6_FILTER_SETPASS(ICMP6_TIME_EXCEEDED, &filter); + ICMP6_FILTER_SETPASS(ICMP6_PARAM_PROB, &filter); + + if (niquery_is_enabled()) + ICMP6_FILTER_SETPASS(ICMPV6_NI_REPLY, &filter); + else + ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filter); + + err = setsockopt(sock->fd, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, sizeof filter); + + if (err < 0) { + perror("setsockopt(ICMP6_FILTER)"); + exit(2); + } + } + + if (options & F_NOLOOP) { + int loop = 0; + if (setsockopt(sock->fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &loop, sizeof loop) == -1) { + perror ("can't disable multicast loopback"); + exit(2); + } + } + if (options & F_TTL) { + if (setsockopt(sock->fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof ttl) == -1) { + perror ("can't set multicast hop limit"); + exit(2); + } + if (setsockopt(sock->fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof ttl) == -1) { + perror ("can't set unicast hop limit"); + exit(2); + } + } + + const int on = 1; + if ( +#ifdef IPV6_RECVHOPLIMIT + setsockopt(sock->fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, sizeof on) == -1 && + setsockopt(sock->fd, IPPROTO_IPV6, IPV6_2292HOPLIMIT, &on, sizeof on) == -1 +#else + setsockopt(sock->fd, IPPROTO_IPV6, IPV6_HOPLIMIT, &on, sizeof on) == -1 +#endif + ){ + perror ("can't receive hop limit"); + exit(2); + } + + if (options & F_TCLASS) { +#ifdef IPV6_TCLASS + if (setsockopt(sock->fd, IPPROTO_IPV6, IPV6_TCLASS, &tclass, sizeof tclass) == -1) { + perror ("setsockopt(IPV6_TCLASS)"); + exit(2); + } +#else + fprintf(stderr, "Traffic class is not supported.\n"); +#endif + } + + if (options&F_FLOWINFO) { +#ifdef IPV6_FLOWLABEL_MGR + char freq_buf[CMSG_ALIGN(sizeof(struct in6_flowlabel_req)) + cmsglen]; + struct in6_flowlabel_req *freq = (struct in6_flowlabel_req *)freq_buf; + int freq_len = sizeof(*freq); +#ifdef ENABLE_PING6_RTHDR + if (srcrt) + freq_len = CMSG_ALIGN(sizeof(*freq)) + srcrt->cmsg_len; +#endif + memset(freq, 0, sizeof(*freq)); + freq->flr_label = htonl(flowlabel & IPV6_FLOWINFO_FLOWLABEL); + freq->flr_action = IPV6_FL_A_GET; + freq->flr_flags = IPV6_FL_F_CREATE; + freq->flr_share = IPV6_FL_S_EXCL; + memcpy(&freq->flr_dst, &whereto.sin6_addr, 16); +#ifdef ENABLE_PING6_RTHDR + if (srcrt) + memcpy(freq_buf + CMSG_ALIGN(sizeof(*freq)), srcrt, srcrt->cmsg_len); +#endif + if (setsockopt(sock->fd, IPPROTO_IPV6, IPV6_FLOWLABEL_MGR, freq, freq_len) == -1) { + perror ("can't set flowlabel"); + exit(2); + } + flowlabel = freq->flr_label; +#ifdef ENABLE_PING6_RTHDR + if (srcrt) { + cmsglen = (char*)srcrt - (char*)cmsgbuf; + srcrt = NULL; + } +#endif +#else + fprintf(stderr, "Flow labels are not supported.\n"); + exit(2); +#endif + +#ifdef IPV6_FLOWINFO_SEND + whereto.sin6_flowinfo = flowlabel; + if (setsockopt(sock->fd, IPPROTO_IPV6, IPV6_FLOWINFO_SEND, &on, sizeof on) == -1) { + perror ("can't send flowinfo"); + exit(2); + } +#else + fprintf(stderr, "Flowinfo is not supported.\n"); + exit(2); +#endif + } + + printf("PING %s(%s) ", hostname, pr_addr(&whereto, sizeof whereto)); + if (flowlabel) + printf(", flow 0x%05x, ", (unsigned)ntohl(flowlabel)); + if (device || (options&F_STRICTSOURCE)) { + int saved_options = options; + + options |= F_NUMERIC; + printf("from %s %s: ", pr_addr(&source6, sizeof source6), device ? : ""); + options = saved_options; + } + printf("%d data bytes\n", datalen); + + setup(sock); + + drop_capabilities(); + + main_loop(&ping6_func_set, sock, packet, packlen); +} + +int ping6_receive_error_msg(socket_st *sock) +{ + int res; + char cbuf[512]; + struct iovec iov; + struct msghdr msg; + struct cmsghdr *cmsg; + struct sock_extended_err *e; + struct icmp6_hdr icmph; + struct sockaddr_in6 target; + int net_errors = 0; + int local_errors = 0; + int saved_errno = errno; + + iov.iov_base = &icmph; + iov.iov_len = sizeof(icmph); + msg.msg_name = (void*)⌖ + msg.msg_namelen = sizeof(target); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_flags = 0; + msg.msg_control = cbuf; + msg.msg_controllen = sizeof(cbuf); + + res = recvmsg(sock->fd, &msg, MSG_ERRQUEUE|MSG_DONTWAIT); + if (res < 0) + goto out; + + e = NULL; + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level == IPPROTO_IPV6) { + if (cmsg->cmsg_type == IPV6_RECVERR) + e = (struct sock_extended_err *)CMSG_DATA(cmsg); + } + } + if (e == NULL) + abort(); + + if (e->ee_origin == SO_EE_ORIGIN_LOCAL) { + local_errors++; + if (options & F_QUIET) + goto out; + if (options & F_FLOOD) + write_stdout("E", 1); + else if (e->ee_errno != EMSGSIZE) + fprintf(stderr, "ping: local error: %s\n", strerror(e->ee_errno)); + else + fprintf(stderr, "ping: local error: Message too long, mtu=%u\n", e->ee_info); + nerrors++; + } else if (e->ee_origin == SO_EE_ORIGIN_ICMP6) { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)(e+1); + + if (res < sizeof(icmph) || + memcmp(&target.sin6_addr, &whereto.sin6_addr, 16) || + icmph.icmp6_type != ICMP6_ECHO_REQUEST || + !is_ours(sock, icmph.icmp6_id)) { + /* Not our error, not an error at all. Clear. */ + saved_errno = 0; + goto out; + } + + net_errors++; + nerrors++; + if (options & F_QUIET) + goto out; + if (options & F_FLOOD) { + write_stdout("\bE", 2); + } else { + print_timestamp(); + printf("From %s icmp_seq=%u ", pr_addr(sin6, sizeof *sin6), ntohs(icmph.icmp6_seq)); + pr_icmph(e->ee_type, e->ee_code, e->ee_info); + putchar('\n'); + fflush(stdout); + } + } + +out: + errno = saved_errno; + return net_errors ? : -local_errors; +} + +/* + * pinger -- + * Compose and transmit an ICMP ECHO REQUEST packet. The IP packet + * will be added on by the kernel. The ID field is our UNIX process ID, + * and the sequence number is an ascending integer. The first 8 bytes + * of the data portion are used to hold a UNIX "timeval" struct in VAX + * byte-order, to compute the round-trip time. + */ +int build_echo(__u8 *_icmph, unsigned packet_size) +{ + struct icmp6_hdr *icmph; + int cc; + + icmph = (struct icmp6_hdr *)_icmph; + icmph->icmp6_type = ICMP6_ECHO_REQUEST; + icmph->icmp6_code = 0; + icmph->icmp6_cksum = 0; + icmph->icmp6_seq = htons(ntransmitted+1); + icmph->icmp6_id = ident; + + if (timing) + gettimeofday((struct timeval *)&_icmph[8], + (struct timezone *)NULL); + + cc = datalen + 8; /* skips ICMP portion */ + + return cc; +} + + +int build_niquery(__u8 *_nih, unsigned packet_size) +{ + struct ni_hdr *nih; + int cc; + + nih = (struct ni_hdr *)_nih; + nih->ni_cksum = 0; + + nih->ni_type = ICMPV6_NI_QUERY; + cc = sizeof(*nih); + datalen = 0; + + niquery_fill_nonce(ntransmitted + 1, nih->ni_nonce); + nih->ni_code = ni_subject_type; + nih->ni_qtype = htons(ni_query); + nih->ni_flags = ni_flag; + memcpy(nih + 1, ni_subject, ni_subject_len); + cc += ni_subject_len; + + return cc; +} + +int ping6_send_probe(socket_st *sock, void *packet, unsigned packet_size) +{ + int len, cc; + + rcvd_clear(ntransmitted + 1); + + if (niquery_is_enabled()) + len = build_niquery(packet, packet_size); + else + len = build_echo(packet, packet_size); + + if (cmsglen == 0) { + cc = sendto(sock->fd, (char *)packet, len, confirm, + (struct sockaddr *) &whereto, + sizeof(struct sockaddr_in6)); + } else { + struct msghdr mhdr; + struct iovec iov; + + iov.iov_len = len; + iov.iov_base = packet; + + memset(&mhdr, 0, sizeof(mhdr)); + mhdr.msg_name = &whereto; + mhdr.msg_namelen = sizeof(struct sockaddr_in6); + mhdr.msg_iov = &iov; + mhdr.msg_iovlen = 1; + mhdr.msg_control = cmsgbuf; + mhdr.msg_controllen = cmsglen; + + cc = sendmsg(sock->fd, &mhdr, confirm); + } + confirm = 0; + + return (cc == len ? 0 : cc); +} + +void pr_echo_reply(__u8 *_icmph, int cc) +{ + struct icmp6_hdr *icmph = (struct icmp6_hdr *) _icmph; + printf(" icmp_seq=%u", ntohs(icmph->icmp6_seq)); +}; + +static void putchar_safe(char c) +{ + if (isprint(c)) + putchar(c); + else + printf("\\%03o", c); +} + +static +void pr_niquery_reply_name(struct ni_hdr *nih, int len) +{ + __u8 *h = (__u8 *)(nih + 1); + __u8 *p = h + 4; + __u8 *end = (__u8 *)nih + len; + int continued = 0; + char buf[1024]; + int ret; + + len -= sizeof(struct ni_hdr) + 4; + + if (len < 0) { + printf(" parse error (too short)"); + return; + } + while (p < end) { + int fqdn = 1; + int i; + + memset(buf, 0xff, sizeof(buf)); + + if (continued) + putchar(','); + + ret = dn_expand(h, end, p, buf, sizeof(buf)); + if (ret < 0) { + printf(" parse error (truncated)"); + break; + } + if (p + ret < end && *(p + ret) == '\0') + fqdn = 0; + + putchar(' '); + for (i = 0; i < strlen(buf); i++) + putchar_safe(buf[i]); + if (fqdn) + putchar('.'); + + p += ret + !fqdn; + + continued = 1; + } +} + +static +void pr_niquery_reply_addr(struct ni_hdr *nih, int len) +{ + __u8 *h = (__u8 *)(nih + 1); + __u8 *p; + __u8 *end = (__u8 *)nih + len; + int af; + int aflen; + int continued = 0; + int truncated; + char buf[1024]; + + switch (ntohs(nih->ni_qtype)) { + case NI_QTYPE_IPV4ADDR: + af = AF_INET; + aflen = sizeof(struct in_addr); + truncated = nih->ni_flags & NI_IPV6ADDR_F_TRUNCATE; + break; + case NI_QTYPE_IPV6ADDR: + af = AF_INET6; + aflen = sizeof(struct in6_addr); + truncated = nih->ni_flags & NI_IPV4ADDR_F_TRUNCATE; + break; + default: + /* should not happen */ + af = aflen = truncated = 0; + } + p = h; + if (len < 0) { + printf(" parse error (too short)"); + return; + } + + while (p < end) { + if (continued) + putchar(','); + + if (p + sizeof(__u32) + aflen > end) { + printf(" parse error (truncated)"); + break; + } + if (!inet_ntop(af, p + sizeof(__u32), buf, sizeof(buf))) + printf(" unexpeced error in inet_ntop(%s)", + strerror(errno)); + else + printf(" %s", buf); + p += sizeof(__u32) + aflen; + + continued = 1; + } + if (truncated) + printf(" (truncated)"); +} + +static +void pr_niquery_reply(__u8 *_nih, int len) +{ + struct ni_hdr *nih = (struct ni_hdr *)_nih; + + switch (nih->ni_code) { + case NI_SUCCESS: + switch (ntohs(nih->ni_qtype)) { + case NI_QTYPE_NAME: + pr_niquery_reply_name(nih, len); + break; + case NI_QTYPE_IPV4ADDR: + case NI_QTYPE_IPV6ADDR: + pr_niquery_reply_addr(nih, len); + break; + default: + printf(" unknown qtype(0x%02x)", ntohs(nih->ni_qtype)); + } + break; + case NI_REFUSED: + printf(" refused"); + break; + case NI_UNKNOWN: + printf(" unknown"); + break; + default: + printf(" unknown code(%02x)", ntohs(nih->ni_code)); + } + printf("; seq=%u;", ntohsp((__u16*)nih->ni_nonce)); +} + +/* + * parse_reply -- + * Print out the packet, if it came from us. This logic is necessary + * because ALL readers of the ICMP socket get a copy of ALL ICMP packets + * which arrive ('tis only fair). This permits multiple copies of this + * program to be run without having intermingled output (or statistics!). + */ +int +ping6_parse_reply(socket_st *sock, struct msghdr *msg, int cc, void *addr, struct timeval *tv) +{ + struct sockaddr_in6 *from = addr; + __u8 *buf = msg->msg_iov->iov_base; + struct cmsghdr *c; + struct icmp6_hdr *icmph; + int hops = -1; + + for (c = CMSG_FIRSTHDR(msg); c; c = CMSG_NXTHDR(msg, c)) { + if (c->cmsg_level != IPPROTO_IPV6) + continue; + switch(c->cmsg_type) { + case IPV6_HOPLIMIT: +#ifdef IPV6_2292HOPLIMIT + case IPV6_2292HOPLIMIT: +#endif + if (c->cmsg_len < CMSG_LEN(sizeof(int))) + continue; + memcpy(&hops, CMSG_DATA(c), sizeof(hops)); + } + } + + + /* Now the ICMP part */ + + icmph = (struct icmp6_hdr *) buf; + if (cc < 8) { + if (options & F_VERBOSE) + fprintf(stderr, "ping: packet too short (%d bytes)\n", cc); + return 1; + } + + if (icmph->icmp6_type == ICMP6_ECHO_REPLY) { + if (!is_ours(sock, icmph->icmp6_id)) + return 1; + if (!contains_pattern_in_payload((__u8*)(icmph+1))) + return 1; /* 'Twas really not our ECHO */ + if (gather_statistics((__u8*)icmph, sizeof(*icmph), cc, + ntohs(icmph->icmp6_seq), + hops, 0, tv, pr_addr(from, sizeof *from), + pr_echo_reply)) { + fflush(stdout); + return 0; + } + } else if (icmph->icmp6_type == ICMPV6_NI_REPLY) { + struct ni_hdr *nih = (struct ni_hdr *)icmph; + int seq = niquery_check_nonce(nih->ni_nonce); + if (seq < 0) + return 1; + if (gather_statistics((__u8*)icmph, sizeof(*icmph), cc, + seq, + hops, 0, tv, pr_addr(from, sizeof *from), + pr_niquery_reply)) + return 0; + } else { + int nexthdr; + struct ip6_hdr *iph1 = (struct ip6_hdr*)(icmph+1); + struct icmp6_hdr *icmph1 = (struct icmp6_hdr *)(iph1+1); + + /* We must not ever fall here. All the messages but + * echo reply are blocked by filter and error are + * received with IPV6_RECVERR. Ugly code is preserved + * however, just to remember what crap we avoided + * using RECVRERR. :-) + */ + + if (cc < 8+sizeof(struct ip6_hdr)+8) + return 1; + + if (memcmp(&iph1->ip6_dst, &whereto.sin6_addr, 16)) + return 1; + + nexthdr = iph1->ip6_nxt; + + if (nexthdr == 44) { + nexthdr = *(__u8*)icmph1; + icmph1++; + } + if (nexthdr == IPPROTO_ICMPV6) { + if (icmph1->icmp6_type != ICMP6_ECHO_REQUEST || + !is_ours(sock, icmph1->icmp6_id)) + return 1; + acknowledge(ntohs(icmph1->icmp6_seq)); + nerrors++; + if (options & F_FLOOD) { + write_stdout("\bE", 2); + return 0; + } + print_timestamp(); + printf("From %s: icmp_seq=%u ", pr_addr(from, sizeof *from), ntohs(icmph1->icmp6_seq)); + } else { + /* We've got something other than an ECHOREPLY */ + if (!(options & F_VERBOSE) || uid) + return 1; + print_timestamp(); + printf("From %s: ", pr_addr(from, sizeof *from)); + } + pr_icmph(icmph->icmp6_type, icmph->icmp6_code, ntohl(icmph->icmp6_mtu)); + } + + if (options & F_AUDIBLE) { + putchar('\a'); + if(options & F_FLOOD) + fflush(stdout); + } + if (!(options & F_FLOOD)) { + putchar('\n'); + fflush(stdout); + } + return 0; +} + + +int pr_icmph(__u8 type, __u8 code, __u32 info) +{ + switch(type) { + case ICMP6_DST_UNREACH: + printf("Destination unreachable: "); + switch (code) { + case ICMP6_DST_UNREACH_NOROUTE: + printf("No route"); + break; + case ICMP6_DST_UNREACH_ADMIN: + printf("Administratively prohibited"); + break; + case ICMP6_DST_UNREACH_BEYONDSCOPE: + printf("Beyond scope of source address"); + break; + case ICMP6_DST_UNREACH_ADDR: + printf("Address unreachable"); + break; + case ICMP6_DST_UNREACH_NOPORT: + printf("Port unreachable"); + break; + default: + printf("Unknown code %d", code); + break; + } + break; + case ICMP6_PACKET_TOO_BIG: + printf("Packet too big: mtu=%u", info); + if (code) + printf(", code=%d", code); + break; + case ICMP6_TIME_EXCEEDED: + printf("Time exceeded: "); + if (code == ICMP6_TIME_EXCEED_TRANSIT) + printf("Hop limit"); + else if (code == ICMP6_TIME_EXCEED_REASSEMBLY) + printf("Defragmentation failure"); + else + printf("code %d", code); + break; + case ICMP6_PARAM_PROB: + printf("Parameter problem: "); + if (code == ICMP6_PARAMPROB_HEADER) + printf("Wrong header field "); + else if (code == ICMP6_PARAMPROB_NEXTHEADER) + printf("Unknown header "); + else if (code == ICMP6_PARAMPROB_OPTION) + printf("Unknown option "); + else + printf("code %d ", code); + printf ("at %u", info); + break; + case ICMP6_ECHO_REQUEST: + printf("Echo request"); + break; + case ICMP6_ECHO_REPLY: + printf("Echo reply"); + break; + case MLD_LISTENER_QUERY: + printf("MLD Query"); + break; + case MLD_LISTENER_REPORT: + printf("MLD Report"); + break; + case MLD_LISTENER_REDUCTION: + printf("MLD Reduction"); + break; + default: + printf("unknown icmp type: %u", type); + + } + return 0; +} + +void ping6_install_filter(socket_st *sock) +{ + static int once; + static struct sock_filter insns[] = { + BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 4), /* Load icmp echo ident */ + BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0xAAAA, 0, 1), /* Ours? */ + BPF_STMT(BPF_RET|BPF_K, ~0U), /* Yes, it passes. */ + BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 0), /* Load icmp type */ + BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, ICMP6_ECHO_REPLY, 1, 0), /* Echo? */ + BPF_STMT(BPF_RET|BPF_K, ~0U), /* No. It passes. This must not happen. */ + BPF_STMT(BPF_RET|BPF_K, 0), /* Echo with wrong ident. Reject. */ + }; + static struct sock_fprog filter = { + sizeof insns / sizeof(insns[0]), + insns + }; + + if (once) + return; + once = 1; + + /* Patch bpflet for current identifier. */ + insns[1] = (struct sock_filter)BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htons(ident), 0, 1); + + if (setsockopt(sock->fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter))) + perror("WARNING: failed to install socket filter\n"); +} + +#define USAGE_NEWLINE "\n " + +void ping6_usage(unsigned from_ping) +{ + const char *name; + if (from_ping) + name = "ping -6"; + else + name = "ping6"; + fprintf(stderr, + "Usage: %s" + " [-" + "aAbBdDfhLnOqrRUvV" + "]" + " [-c count]" + " [-i interval]" + " [-I interface]" + USAGE_NEWLINE + " [-l preload]" + " [-m mark]" + " [-M pmtudisc_option]" + USAGE_NEWLINE + " [-N nodeinfo_option]" + " [-p pattern]" + " [-Q tclass]" + " [-s packetsize]" + USAGE_NEWLINE + " [-S sndbuf]" + " [-t ttl]" + " [-T timestamp_option]" + " [-w deadline]" + USAGE_NEWLINE + " [-W timeout]" +#ifdef ENABLE_PING6_RTHDR + " [hop1 ...]" +#endif + " destination" + "\n", name + ); + exit(2); +} diff --git a/ping_common.c b/ping_common.c new file mode 100644 index 0000000..040bf23 --- /dev/null +++ b/ping_common.c @@ -0,0 +1,997 @@ +/* + * Copyright (c) 1989 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Mike Muuss. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "ping.h" + +#ifndef HZ +#define HZ sysconf(_SC_CLK_TCK) +#endif + +int options; + +int mark; +int sndbuf; +int ttl; +int rtt; +int rtt_addend; +__u16 acked; + +unsigned char outpack[MAXPACKET]; +struct rcvd_table rcvd_tbl; + +/* counters */ +long npackets; /* max packets to transmit */ +long nreceived; /* # of packets we got back */ +long nrepeats; /* number of duplicates */ +long ntransmitted; /* sequence # for outbound packets = #sent */ +long nchecksum; /* replies with bad checksum */ +long nerrors; /* icmp errors */ +int interval = 1000; /* interval between packets (msec) */ +int preload = 1; +int deadline = 0; /* time to die */ +int lingertime = MAXWAIT*1000; +struct timeval start_time, cur_time; +volatile int exiting; +volatile int status_snapshot; +int confirm = 0; +volatile int in_pr_addr = 0; /* pr_addr() is executing */ +jmp_buf pr_addr_jmp; + +/* Stupid workarounds for bugs/missing functionality in older linuces. + * confirm_flag fixes refusing service of kernels without MSG_CONFIRM. + * i.e. for linux-2.2 */ +int confirm_flag = MSG_CONFIRM; + +/* timing */ +int timing; /* flag to do timing */ +long tmin = LONG_MAX; /* minimum round trip time */ +long tmax; /* maximum round trip time */ +/* Message for rpm maintainers: have _shame_. If you want + * to fix something send the patch to me for sanity checking. + * "sparcfix" patch is a complete non-sense, apparenly the person + * prepared it was stoned. + */ +long long tsum; /* sum of all times, for doing average */ +long long tsum2; +int pipesize = -1; + +int datalen = DEFDATALEN; + +char *hostname; +int uid; +uid_t euid; +int ident; /* process id to identify our packets */ + +static int screen_width = INT_MAX; + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) + +#ifdef CAPABILITIES +static cap_value_t cap_raw = CAP_NET_RAW; +static cap_value_t cap_admin = CAP_NET_ADMIN; +#endif + +void limit_capabilities(void) +{ +#ifdef CAPABILITIES + cap_t cap_cur_p; + cap_t cap_p; + cap_flag_value_t cap_ok; + + cap_cur_p = cap_get_proc(); + if (!cap_cur_p) { + perror("ping: cap_get_proc"); + exit(-1); + } + + cap_p = cap_init(); + if (!cap_p) { + perror("ping: cap_init"); + exit(-1); + } + + cap_ok = CAP_CLEAR; + cap_get_flag(cap_cur_p, CAP_NET_ADMIN, CAP_PERMITTED, &cap_ok); + + if (cap_ok != CAP_CLEAR) + cap_set_flag(cap_p, CAP_PERMITTED, 1, &cap_admin, CAP_SET); + + cap_ok = CAP_CLEAR; + cap_get_flag(cap_cur_p, CAP_NET_RAW, CAP_PERMITTED, &cap_ok); + + if (cap_ok != CAP_CLEAR) + cap_set_flag(cap_p, CAP_PERMITTED, 1, &cap_raw, CAP_SET); + + if (cap_set_proc(cap_p) < 0) { + perror("ping: cap_set_proc"); + exit(-1); + } + + if (prctl(PR_SET_KEEPCAPS, 1) < 0) { + perror("ping: prctl"); + exit(-1); + } + + if (setuid(getuid()) < 0) { + perror("setuid"); + exit(-1); + } + + if (prctl(PR_SET_KEEPCAPS, 0) < 0) { + perror("ping: prctl"); + exit(-1); + } + + cap_free(cap_p); + cap_free(cap_cur_p); +#endif + uid = getuid(); + euid = geteuid(); +#ifndef CAPABILITIES + if (seteuid(uid)) { + perror("ping: setuid"); + exit(-1); + } +#endif +} + +#ifdef CAPABILITIES +int modify_capability(cap_value_t cap, cap_flag_value_t on) +{ + cap_t cap_p = cap_get_proc(); + cap_flag_value_t cap_ok; + int rc = -1; + + if (!cap_p) { + perror("ping: cap_get_proc"); + goto out; + } + + cap_ok = CAP_CLEAR; + cap_get_flag(cap_p, cap, CAP_PERMITTED, &cap_ok); + if (cap_ok == CAP_CLEAR) { + rc = on ? -1 : 0; + goto out; + } + + cap_set_flag(cap_p, CAP_EFFECTIVE, 1, &cap, on); + + if (cap_set_proc(cap_p) < 0) { + perror("ping: cap_set_proc"); + goto out; + } + + cap_free(cap_p); + cap_p = NULL; + + rc = 0; +out: + if (cap_p) + cap_free(cap_p); + return rc; +} +#else +int modify_capability(int on) +{ + if (seteuid(on ? euid : getuid())) { + perror("seteuid"); + return -1; + } + + return 0; +} +#endif + +void drop_capabilities(void) +{ +#ifdef CAPABILITIES + cap_t cap = cap_init(); + if (cap_set_proc(cap) < 0) { + perror("ping: cap_set_proc"); + exit(-1); + } + cap_free(cap); +#else + if (setuid(getuid())) { + perror("ping: setuid"); + exit(-1); + } +#endif +} + +/* Fills all the outpack, excluding ICMP header, but _including_ + * timestamp area with supplied pattern. + */ +void fill(char *patp, void *packet, unsigned packet_size) +{ + int ii, jj, kk; + int pat[16]; + char *cp; + unsigned char *bp = packet+8; + +#ifdef USE_IDN + setlocale(LC_ALL, "C"); +#endif + + for (cp = patp; *cp; cp++) { + if (!isxdigit(*cp)) { + fprintf(stderr, + "ping: patterns must be specified as hex digits.\n"); + exit(2); + } + } + ii = sscanf(patp, + "%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x", + &pat[0], &pat[1], &pat[2], &pat[3], &pat[4], &pat[5], &pat[6], + &pat[7], &pat[8], &pat[9], &pat[10], &pat[11], &pat[12], + &pat[13], &pat[14], &pat[15]); + + if (ii > 0) { + for (kk = 0; kk <= packet_size - (8 + ii); kk += ii) + for (jj = 0; jj < ii; ++jj) + bp[jj + kk] = pat[jj]; + } + if (!(options & F_QUIET)) { + printf("PATTERN: 0x"); + for (jj = 0; jj < ii; ++jj) + printf("%02x", bp[jj] & 0xFF); + printf("\n"); + } + +#ifdef USE_IDN + setlocale(LC_ALL, ""); +#endif +} + +static void sigexit(int signo) +{ + exiting = 1; + if (in_pr_addr) + longjmp(pr_addr_jmp, 0); +} + +static void sigstatus(int signo) +{ + status_snapshot = 1; +} + + +int __schedule_exit(int next) +{ + static unsigned long waittime; + struct itimerval it; + + if (waittime) + return next; + + if (nreceived) { + waittime = 2 * tmax; + if (waittime < 1000*interval) + waittime = 1000*interval; + } else + waittime = lingertime*1000; + + if (next < 0 || next < waittime/1000) + next = waittime/1000; + + it.it_interval.tv_sec = 0; + it.it_interval.tv_usec = 0; + it.it_value.tv_sec = waittime/1000000; + it.it_value.tv_usec = waittime%1000000; + setitimer(ITIMER_REAL, &it, NULL); + return next; +} + +static inline void update_interval(void) +{ + int est = rtt ? rtt/8 : interval*1000; + + interval = (est+rtt_addend+500)/1000; + if (uid && interval < MINUSERINTERVAL) + interval = MINUSERINTERVAL; +} + +/* + * Print timestamp + */ +void print_timestamp(void) +{ + if (options & F_PTIMEOFDAY) { + struct timeval tv; + gettimeofday(&tv, NULL); + printf("[%lu.%06lu] ", + (unsigned long)tv.tv_sec, (unsigned long)tv.tv_usec); + } +} + +/* + * pinger -- + * Compose and transmit an ICMP ECHO REQUEST packet. The IP packet + * will be added on by the kernel. The ID field is our UNIX process ID, + * and the sequence number is an ascending integer. The first 8 bytes + * of the data portion are used to hold a UNIX "timeval" struct in VAX + * byte-order, to compute the round-trip time. + */ +int pinger(ping_func_set_st *fset, socket_st *sock) +{ + static int oom_count; + static int tokens; + int i; + + /* Have we already sent enough? If we have, return an arbitrary positive value. */ + if (exiting || (npackets && ntransmitted >= npackets && !deadline)) + return 1000; + + /* Check that packets < rate*time + preload */ + if (cur_time.tv_sec == 0) { + gettimeofday(&cur_time, NULL); + tokens = interval*(preload-1); + } else { + long ntokens; + struct timeval tv; + + gettimeofday(&tv, NULL); + ntokens = (tv.tv_sec - cur_time.tv_sec)*1000 + + (tv.tv_usec-cur_time.tv_usec)/1000; + if (!interval) { + /* Case of unlimited flood is special; + * if we see no reply, they are limited to 100pps */ + if (ntokens < MININTERVAL && in_flight() >= preload) + return MININTERVAL-ntokens; + } + ntokens += tokens; + if (ntokens > interval*preload) + ntokens = interval*preload; + if (ntokens < interval) + return interval - ntokens; + + cur_time = tv; + tokens = ntokens - interval; + } + + if (options & F_OUTSTANDING) { + if (ntransmitted > 0 && !rcvd_test(ntransmitted)) { + print_timestamp(); + printf("no answer yet for icmp_seq=%lu\n", (ntransmitted % MAX_DUP_CHK)); + fflush(stdout); + } + } + +resend: + i = fset->send_probe(sock, outpack, sizeof(outpack)); + + if (i == 0) { + oom_count = 0; + advance_ntransmitted(); + if (!(options & F_QUIET) && (options & F_FLOOD)) { + /* Very silly, but without this output with + * high preload or pipe size is very confusing. */ + if ((preload < screen_width && pipesize < screen_width) || + in_flight() < screen_width) + write_stdout(".", 1); + } + return interval - tokens; + } + + /* And handle various errors... */ + if (i > 0) { + /* Apparently, it is some fatal bug. */ + abort(); + } else if (errno == ENOBUFS || errno == ENOMEM) { + int nores_interval; + + /* Device queue overflow or OOM. Packet is not sent. */ + tokens = 0; + /* Slowdown. This works only in adaptive mode (option -A) */ + rtt_addend += (rtt < 8*50000 ? rtt/8 : 50000); + if (options&F_ADAPTIVE) + update_interval(); + nores_interval = SCHINT(interval/2); + if (nores_interval > 500) + nores_interval = 500; + oom_count++; + if (oom_count*nores_interval < lingertime) + return nores_interval; + i = 0; + /* Fall to hard error. It is to avoid complete deadlock + * on stuck output device even when dealine was not requested. + * Expected timings are screwed up in any case, but we will + * exit some day. :-) */ + } else if (errno == EAGAIN) { + /* Socket buffer is full. */ + tokens += interval; + return MININTERVAL; + } else { + if ((i=fset->receive_error_msg(sock)) > 0) { + /* An ICMP error arrived. In this case, we've received + * an error from sendto(), but we've also received an + * ICMP message, which means the packet did in fact + * send in some capacity. So, in this odd case, report + * the more specific errno as the error, and treat this + * as a hard local error. */ + i = 0; + goto hard_local_error; + } + /* Compatibility with old linuces. */ + if (i == 0 && confirm_flag && errno == EINVAL) { + confirm_flag = 0; + errno = 0; + } + if (!errno) + goto resend; + } + +hard_local_error: + /* Hard local error. Pretend we sent packet. */ + advance_ntransmitted(); + + if (i == 0 && !(options & F_QUIET)) { + if (options & F_FLOOD) + write_stdout("E", 1); + else + perror("ping: sendmsg"); + } + tokens = 0; + return SCHINT(interval); +} + +/* Set socket buffers, "alloc" is an estimate of memory taken by single packet. */ + +void sock_setbufs(socket_st *sock, int alloc) +{ + int rcvbuf, hold; + socklen_t tmplen = sizeof(hold); + + if (!sndbuf) + sndbuf = alloc; + setsockopt(sock->fd, SOL_SOCKET, SO_SNDBUF, (char *)&sndbuf, sizeof(sndbuf)); + + rcvbuf = hold = alloc * preload; + if (hold < 65536) + hold = 65536; + setsockopt(sock->fd, SOL_SOCKET, SO_RCVBUF, (char *)&hold, sizeof(hold)); + if (getsockopt(sock->fd, SOL_SOCKET, SO_RCVBUF, (char *)&hold, &tmplen) == 0) { + if (hold < rcvbuf) + fprintf(stderr, "WARNING: probably, rcvbuf is not enough to hold preload.\n"); + } +} + +/* Protocol independent setup and parameter checks. */ + +void setup(socket_st *sock) +{ + int hold; + struct timeval tv; + sigset_t sset; + + if ((options & F_FLOOD) && !(options & F_INTERVAL)) + interval = 0; + + if (uid && interval < MINUSERINTERVAL) { + fprintf(stderr, "ping: cannot flood; minimal interval allowed for user is %dms\n", MINUSERINTERVAL); + exit(2); + } + + if (interval >= INT_MAX/preload) { + fprintf(stderr, "ping: illegal preload and/or interval\n"); + exit(2); + } + + hold = 1; + if (options & F_SO_DEBUG) + setsockopt(sock->fd, SOL_SOCKET, SO_DEBUG, (char *)&hold, sizeof(hold)); + if (options & F_SO_DONTROUTE) + setsockopt(sock->fd, SOL_SOCKET, SO_DONTROUTE, (char *)&hold, sizeof(hold)); + +#ifdef SO_TIMESTAMP + if (!(options&F_LATENCY)) { + int on = 1; + if (setsockopt(sock->fd, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(on))) + fprintf(stderr, "Warning: no SO_TIMESTAMP support, falling back to SIOCGSTAMP\n"); + } +#endif +#ifdef SO_MARK + if (options & F_MARK) { + int ret; + + enable_capability_admin(); + ret = setsockopt(sock->fd, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)); + disable_capability_admin(); + + if (ret == -1) { + /* we probably dont wanna exit since old kernels + * dont support mark .. + */ + fprintf(stderr, "Warning: Failed to set mark %d\n", mark); + } + } +#endif + + /* Set some SNDTIMEO to prevent blocking forever + * on sends, when device is too slow or stalls. Just put limit + * of one second, or "interval", if it is less. + */ + tv.tv_sec = 1; + tv.tv_usec = 0; + if (interval < 1000) { + tv.tv_sec = 0; + tv.tv_usec = 1000 * SCHINT(interval); + } + setsockopt(sock->fd, SOL_SOCKET, SO_SNDTIMEO, (char*)&tv, sizeof(tv)); + + /* Set RCVTIMEO to "interval". Note, it is just an optimization + * allowing to avoid redundant poll(). */ + tv.tv_sec = SCHINT(interval)/1000; + tv.tv_usec = 1000*(SCHINT(interval)%1000); + if (setsockopt(sock->fd, SOL_SOCKET, SO_RCVTIMEO, (char*)&tv, sizeof(tv))) + options |= F_FLOOD_POLL; + + if (!(options & F_PINGFILLED)) { + int i; + unsigned char *p = outpack+8; + + /* Do not forget about case of small datalen, + * fill timestamp area too! + */ + for (i = 0; i < datalen; ++i) + *p++ = i; + } + + if (sock->socktype == SOCK_RAW) + ident = htons(getpid() & 0xFFFF); + + set_signal(SIGINT, sigexit); + set_signal(SIGALRM, sigexit); + set_signal(SIGQUIT, sigstatus); + + sigemptyset(&sset); + sigprocmask(SIG_SETMASK, &sset, NULL); + + gettimeofday(&start_time, NULL); + + if (deadline) { + struct itimerval it; + + it.it_interval.tv_sec = 0; + it.it_interval.tv_usec = 0; + it.it_value.tv_sec = deadline; + it.it_value.tv_usec = 0; + setitimer(ITIMER_REAL, &it, NULL); + } + + if (isatty(STDOUT_FILENO)) { + struct winsize w; + + if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1) { + if (w.ws_col > 0) + screen_width = w.ws_col; + } + } +} + +/* + * Return 0 if pattern in payload point to be ptr did not match the pattern that was sent + */ +int contains_pattern_in_payload(__u8 *ptr) +{ + int i; + __u8 *cp, *dp; + + /* check the data */ + cp = ((u_char*)ptr) + sizeof(struct timeval); + dp = &outpack[8 + sizeof(struct timeval)]; + for (i = sizeof(struct timeval); i < datalen; ++i, ++cp, ++dp) { + if (*cp != *dp) + return 0; + } + return 1; +} + +void main_loop(ping_func_set_st *fset, socket_st *sock, __u8 *packet, int packlen) +{ + char addrbuf[128]; + char ans_data[4096]; + struct iovec iov; + struct msghdr msg; + struct cmsghdr *c; + int cc; + int next; + int polling; + int recv_error; + + iov.iov_base = (char *)packet; + + for (;;) { + /* Check exit conditions. */ + if (exiting) + break; + if (npackets && nreceived + nerrors >= npackets) + break; + if (deadline && nerrors) + break; + /* Check for and do special actions. */ + if (status_snapshot) + status(); + + /* Send probes scheduled to this time. */ + do { + next = pinger(fset, sock); + next = schedule_exit(next); + } while (next <= 0); + + /* "next" is time to send next probe, if positive. + * If next<=0 send now or as soon as possible. */ + + /* Technical part. Looks wicked. Could be dropped, + * if everyone used the newest kernel. :-) + * Its purpose is: + * 1. Provide intervals less than resolution of scheduler. + * Solution: spinning. + * 2. Avoid use of poll(), when recvmsg() can provide + * timed waiting (SO_RCVTIMEO). */ + polling = 0; + recv_error = 0; + if ((options & (F_ADAPTIVE|F_FLOOD_POLL)) || nextfd; + pset.events = POLLIN; + pset.revents = 0; + if (poll(&pset, 1, next) < 1 || + !(pset.revents&(POLLIN|POLLERR))) + continue; + polling = MSG_DONTWAIT; + recv_error = pset.revents&POLLERR; + } + } + + for (;;) { + struct timeval *recv_timep = NULL; + struct timeval recv_time; + int not_ours = 0; /* Raw socket can receive messages + * destined to other running pings. */ + + iov.iov_len = packlen; + memset(&msg, 0, sizeof(msg)); + msg.msg_name = addrbuf; + msg.msg_namelen = sizeof(addrbuf); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = ans_data; + msg.msg_controllen = sizeof(ans_data); + + cc = recvmsg(sock->fd, &msg, polling); + polling = MSG_DONTWAIT; + + if (cc < 0) { + /* If there was a POLLERR and there is no packet + * on the socket, try to read the error queue. + * Otherwise, give up. + */ + if ((errno == EAGAIN && !recv_error) || + errno == EINTR) + break; + recv_error = 0; + if (!fset->receive_error_msg(sock)) { + if (errno) { + perror("ping: recvmsg"); + break; + } + not_ours = 1; + } + } else { + +#ifdef SO_TIMESTAMP + for (c = CMSG_FIRSTHDR(&msg); c; c = CMSG_NXTHDR(&msg, c)) { + if (c->cmsg_level != SOL_SOCKET || + c->cmsg_type != SO_TIMESTAMP) + continue; + if (c->cmsg_len < CMSG_LEN(sizeof(struct timeval))) + continue; + recv_timep = (struct timeval*)CMSG_DATA(c); + } +#endif + + if ((options&F_LATENCY) || recv_timep == NULL) { + if ((options&F_LATENCY) || + ioctl(sock->fd, SIOCGSTAMP, &recv_time)) + gettimeofday(&recv_time, NULL); + recv_timep = &recv_time; + } + + not_ours = fset->parse_reply(sock, &msg, cc, addrbuf, recv_timep); + } + + /* See? ... someone runs another ping on this host. */ + if (not_ours && sock->socktype == SOCK_RAW) + fset->install_filter(sock); + + /* If nothing is in flight, "break" returns us to pinger. */ + if (in_flight() == 0) + break; + + /* Otherwise, try to recvmsg() again. recvmsg() + * is nonblocking after the first iteration, so that + * if nothing is queued, it will receive EAGAIN + * and return to pinger. */ + } + } + finish(); +} + +int gather_statistics(__u8 *icmph, int icmplen, + int cc, __u16 seq, int hops, + int csfailed, struct timeval *tv, char *from, + void (*pr_reply)(__u8 *icmph, int cc)) +{ + int dupflag = 0; + long triptime = 0; + __u8 *ptr = icmph + icmplen; + + ++nreceived; + if (!csfailed) + acknowledge(seq); + + if (timing && cc >= 8+sizeof(struct timeval)) { + struct timeval tmp_tv; + memcpy(&tmp_tv, ptr, sizeof(tmp_tv)); + +restamp: + tvsub(tv, &tmp_tv); + triptime = tv->tv_sec * 1000000 + tv->tv_usec; + if (triptime < 0) { + fprintf(stderr, "Warning: time of day goes back (%ldus), taking countermeasures.\n", triptime); + triptime = 0; + if (!(options & F_LATENCY)) { + gettimeofday(tv, NULL); + options |= F_LATENCY; + goto restamp; + } + } + if (!csfailed) { + tsum += triptime; + tsum2 += (long long)triptime * (long long)triptime; + if (triptime < tmin) + tmin = triptime; + if (triptime > tmax) + tmax = triptime; + if (!rtt) + rtt = triptime*8; + else + rtt += triptime-rtt/8; + if (options&F_ADAPTIVE) + update_interval(); + } + } + + if (csfailed) { + ++nchecksum; + --nreceived; + } else if (rcvd_test(seq)) { + ++nrepeats; + --nreceived; + dupflag = 1; + } else { + rcvd_set(seq); + dupflag = 0; + } + confirm = confirm_flag; + + if (options & F_QUIET) + return 1; + + if (options & F_FLOOD) { + if (!csfailed) + write_stdout("\b \b", 3); + else + write_stdout("\bC", 2); + } else { + int i; + __u8 *cp, *dp; + + print_timestamp(); + printf("%d bytes from %s:", cc, from); + + if (pr_reply) + pr_reply(icmph, cc); + + if (hops >= 0) + printf(" ttl=%d", hops); + + if (cc < datalen+8) { + printf(" (truncated)\n"); + return 1; + } + if (timing) { + if (triptime >= 100000) + printf(" time=%ld ms", (triptime+500)/1000); + else if (triptime >= 10000) + printf(" time=%ld.%01ld ms", triptime/1000, + ((triptime%1000)+50)/100); + else if (triptime >= 1000) + printf(" time=%ld.%02ld ms", triptime/1000, + ((triptime%1000)+5)/10); + else + printf(" time=%ld.%03ld ms", triptime/1000, + triptime%1000); + } + if (dupflag) + printf(" (DUP!)"); + if (csfailed) + printf(" (BAD CHECKSUM!)"); + + /* check the data */ + cp = ((unsigned char*)ptr) + sizeof(struct timeval); + dp = &outpack[8 + sizeof(struct timeval)]; + for (i = sizeof(struct timeval); i < datalen; ++i, ++cp, ++dp) { + if (*cp != *dp) { + printf("\nwrong data byte #%d should be 0x%x but was 0x%x", + i, *dp, *cp); + cp = (unsigned char*)ptr + sizeof(struct timeval); + for (i = sizeof(struct timeval); i < datalen; ++i, ++cp) { + if ((i % 32) == sizeof(struct timeval)) + printf("\n#%d\t", i); + printf("%x ", *cp); + } + break; + } + } + } + return 0; +} + +static long llsqrt(long long a) +{ + long long prev = ~((long long)1 << 63); + long long x = a; + + if (x > 0) { + while (x < prev) { + prev = x; + x = (x+(a/x))/2; + } + } + + return (long)x; +} + +/* + * finish -- + * Print out statistics, and give up. + */ +void finish(void) +{ + struct timeval tv = cur_time; + char *comma = ""; + + tvsub(&tv, &start_time); + + putchar('\n'); + fflush(stdout); + printf("--- %s ping statistics ---\n", hostname); + printf("%ld packets transmitted, ", ntransmitted); + printf("%ld received", nreceived); + if (nrepeats) + printf(", +%ld duplicates", nrepeats); + if (nchecksum) + printf(", +%ld corrupted", nchecksum); + if (nerrors) + printf(", +%ld errors", nerrors); + if (ntransmitted) { +#ifdef USE_IDN + setlocale(LC_ALL, "C"); +#endif + printf(", %g%% packet loss", + (float) ((((long long)(ntransmitted - nreceived)) * 100.0) / + ntransmitted)); + printf(", time %ldms", (1000*tv.tv_sec+tv.tv_usec+500)/1000); + } + putchar('\n'); + + if (nreceived && timing) { + long tmdev; + + tsum /= nreceived + nrepeats; + tsum2 /= nreceived + nrepeats; + tmdev = llsqrt(tsum2 - tsum * tsum); + + printf("rtt min/avg/max/mdev = %ld.%03ld/%lu.%03ld/%ld.%03ld/%ld.%03ld ms", + (long)tmin/1000, (long)tmin%1000, + (unsigned long)(tsum/1000), (long)(tsum%1000), + (long)tmax/1000, (long)tmax%1000, + (long)tmdev/1000, (long)tmdev%1000 + ); + comma = ", "; + } + if (pipesize > 1) { + printf("%spipe %d", comma, pipesize); + comma = ", "; + } + if (nreceived && (!interval || (options&(F_FLOOD|F_ADAPTIVE))) && ntransmitted > 1) { + int ipg = (1000000*(long long)tv.tv_sec+tv.tv_usec)/(ntransmitted-1); + printf("%sipg/ewma %d.%03d/%d.%03d ms", + comma, ipg/1000, ipg%1000, rtt/8000, (rtt/8)%1000); + } + putchar('\n'); + exit(!nreceived || (deadline && nreceived < npackets)); +} + + +void status(void) +{ + int loss = 0; + long tavg = 0; + + status_snapshot = 0; + + if (ntransmitted) + loss = (((long long)(ntransmitted - nreceived)) * 100) / ntransmitted; + + fprintf(stderr, "\r%ld/%ld packets, %d%% loss", nreceived, ntransmitted, loss); + + if (nreceived && timing) { + tavg = tsum / (nreceived + nrepeats); + + fprintf(stderr, ", min/avg/ewma/max = %ld.%03ld/%lu.%03ld/%d.%03d/%ld.%03ld ms", + (long)tmin/1000, (long)tmin%1000, + tavg/1000, tavg%1000, + rtt/8000, (rtt/8)%1000, + (long)tmax/1000, (long)tmax%1000 + ); + } + fprintf(stderr, "\n"); +} + +inline int is_ours(socket_st *sock, uint16_t id) { + return sock->socktype == SOCK_DGRAM || id == ident; +} diff --git a/rarpd.c b/rarpd.c new file mode 100644 index 0000000..2e5e22b --- /dev/null +++ b/rarpd.c @@ -0,0 +1,725 @@ +/* + * rarpd.c RARP daemon. + * + * 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. + * + * Authors: Alexey Kuznetsov, + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +int do_reload = 1; + +int debug; +int verbose; +int ifidx; +int allow_offlink; +int only_ethers; +int all_ifaces; +int listen_arp; +char *ifname; +char *tftp_dir = "/etc/tftpboot"; + +extern int ether_ntohost(char *name, unsigned char *ea); +void usage(void) __attribute__((noreturn)); + +struct iflink +{ + struct iflink *next; + int index; + int hatype; + unsigned char lladdr[16]; + char name[IFNAMSIZ]; + struct ifaddr *ifa_list; +} *ifl_list; + +struct ifaddr +{ + struct ifaddr *next; + __u32 prefix; + __u32 mask; + __u32 local; +}; + +struct rarp_map +{ + struct rarp_map *next; + + int ifindex; + int arp_type; + int lladdr_len; + unsigned char lladdr[16]; + __u32 ipaddr; +} *rarp_db; + +void usage() +{ + fprintf(stderr, "Usage: rarpd [ -dveaA ] [ -b tftpdir ] [ interface]\n"); + exit(1); +} + +void load_db(void) +{ +} + +void load_if(void) +{ + int fd; + struct ifreq *ifrp, *ifend; + struct iflink *ifl; + struct ifaddr *ifa; + struct ifconf ifc; + struct ifreq ibuf[256]; + + if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + syslog(LOG_ERR, "socket: %m"); + return; + } + + ifc.ifc_len = sizeof ibuf; + ifc.ifc_buf = (char *)ibuf; + if (ioctl(fd, SIOCGIFCONF, (char *)&ifc) < 0 || + ifc.ifc_len < (int)sizeof(struct ifreq)) { + syslog(LOG_ERR, "SIOCGIFCONF: %m"); + close(fd); + return; + } + + while ((ifl = ifl_list) != NULL) { + while ((ifa = ifl->ifa_list) != NULL) { + ifl->ifa_list = ifa->next; + free(ifa); + } + ifl_list = ifl->next; + free(ifl); + } + + ifend = (struct ifreq *)((char *)ibuf + ifc.ifc_len); + for (ifrp = ibuf; ifrp < ifend; ifrp++) { + __u32 addr; + __u32 mask; + __u32 prefix; + + if (ifrp->ifr_addr.sa_family != AF_INET) + continue; + addr = ((struct sockaddr_in*)&ifrp->ifr_addr)->sin_addr.s_addr; + if (addr == 0) + continue; + if (ioctl(fd, SIOCGIFINDEX, ifrp)) { + syslog(LOG_ERR, "ioctl(SIOCGIFNAME): %m"); + continue; + } + if (ifidx && ifrp->ifr_ifindex != ifidx) + continue; + for (ifl = ifl_list; ifl; ifl = ifl->next) + if (ifl->index == ifrp->ifr_ifindex) + break; + if (ifl == NULL) { + char *p; + int index = ifrp->ifr_ifindex; + + if (ioctl(fd, SIOCGIFHWADDR, ifrp)) { + syslog(LOG_ERR, "ioctl(SIOCGIFHWADDR): %m"); + continue; + } + + ifl = (struct iflink*)malloc(sizeof(*ifl)); + if (ifl == NULL) + continue; + memset(ifl, 0, sizeof(*ifl)); + ifl->next = ifl_list; + ifl_list = ifl; + ifl->index = index; + ifl->hatype = ifrp->ifr_hwaddr.sa_family; + memcpy(ifl->lladdr, ifrp->ifr_hwaddr.sa_data, 14); + strncpy(ifl->name, ifrp->ifr_name, IFNAMSIZ); + p = strchr(ifl->name, ':'); + if (p) + *p = 0; + if (verbose) + syslog(LOG_INFO, "link %s", ifl->name); + } + if (ioctl(fd, SIOCGIFNETMASK, ifrp)) { + syslog(LOG_ERR, "ioctl(SIOCGIFMASK): %m"); + continue; + } + mask = ((struct sockaddr_in*)&ifrp->ifr_netmask)->sin_addr.s_addr; + if (ioctl(fd, SIOCGIFDSTADDR, ifrp)) { + syslog(LOG_ERR, "ioctl(SIOCGIFDSTADDR): %m"); + continue; + } + prefix = ((struct sockaddr_in*)&ifrp->ifr_dstaddr)->sin_addr.s_addr; + for (ifa = ifl->ifa_list; ifa; ifa = ifa->next) { + if (ifa->local == addr && + ifa->prefix == prefix && + ifa->mask == mask) + break; + } + if (ifa == NULL) { + if (mask == 0 || prefix == 0) + continue; + ifa = (struct ifaddr*)malloc(sizeof(*ifa)); + memset(ifa, 0, sizeof(*ifa)); + ifa->local = addr; + ifa->prefix = prefix; + ifa->mask = mask; + ifa->next = ifl->ifa_list; + ifl->ifa_list = ifa; + + if (verbose) { + int i; + __u32 m = ~0U; + for (i=32; i>=0; i--) { + if (htonl(m) == mask) + break; + m <<= 1; + } + if (addr == prefix) { + syslog(LOG_INFO, " addr %s/%d on %s\n", + inet_ntoa(*(struct in_addr*)&addr), i, ifl->name); + } else { + char tmpa[64]; + sprintf(tmpa, "%s", inet_ntoa(*(struct in_addr*)&addr)); + syslog(LOG_INFO, " addr %s %s/%d on %s\n", tmpa, + inet_ntoa(*(struct in_addr*)&prefix), i, ifl->name); + } + } + } + } +} + +void configure(void) +{ + load_if(); + load_db(); +} + +int bootable(__u32 addr) +{ + struct dirent *dent; + DIR *d; + char name[9]; + + sprintf(name, "%08X", (__u32)ntohl(addr)); + d = opendir(tftp_dir); + if (d == NULL) { + syslog(LOG_ERR, "opendir: %m"); + return 0; + } + while ((dent = readdir(d)) != NULL) { + if (strncmp(dent->d_name, name, 8) == 0) + break; + } + closedir(d); + return dent != NULL; +} + +struct ifaddr *select_ipaddr(int ifindex, __u32 *sel_addr, __u32 **alist) +{ + struct iflink *ifl; + struct ifaddr *ifa; + int retry = 0; + int i; + +retry: + for (ifl=ifl_list; ifl; ifl=ifl->next) + if (ifl->index == ifindex) + break; + if (ifl == NULL && !retry) { + retry++; + load_if(); + goto retry; + } + if (ifl == NULL) + return NULL; + + for (i=0; alist[i]; i++) { + __u32 addr = *(alist[i]); + for (ifa=ifl->ifa_list; ifa; ifa=ifa->next) { + if (!((ifa->prefix^addr)&ifa->mask)) { + *sel_addr = addr; + return ifa; + } + } + if (ifa == NULL && retry==0) { + retry++; + load_if(); + goto retry; + } + } + if (i==1 && allow_offlink) { + *sel_addr = *(alist[0]); + return ifl->ifa_list; + } + syslog(LOG_ERR, "Off-link request on %s", ifl->name); + return NULL; +} + +struct rarp_map *rarp_lookup(int ifindex, int hatype, + int halen, unsigned char *lladdr) +{ + struct rarp_map *r; + + for (r=rarp_db; r; r=r->next) { + if (r->arp_type != hatype && r->arp_type != -1) + continue; + if (r->lladdr_len != halen) + continue; + if (r->ifindex != ifindex && r->ifindex != 0) + continue; + if (memcmp(r->lladdr, lladdr, halen) == 0) + break; + } + + if (r == NULL) { + if (hatype == ARPHRD_ETHER && halen == 6) { + struct ifaddr *ifa; + struct hostent *hp; + char ename[256]; + static struct rarp_map emap = { + NULL, + 0, + ARPHRD_ETHER, + 6, + }; + + if (ether_ntohost(ename, lladdr) != 0 || + (hp = gethostbyname(ename)) == NULL) { + if (verbose) + syslog(LOG_INFO, "not found in /etc/ethers"); + return NULL; + } + if (hp->h_addrtype != AF_INET) { + syslog(LOG_ERR, "no IP address"); + return NULL; + } + ifa = select_ipaddr(ifindex, &emap.ipaddr, (__u32 **)hp->h_addr_list); + if (ifa) { + memcpy(emap.lladdr, lladdr, 6); + if (only_ethers || bootable(emap.ipaddr)) + return &emap; + if (verbose) + syslog(LOG_INFO, "not bootable"); + } + } + } + return r; +} + +static int load_arp_bpflet(int fd) +{ + static struct sock_filter insns[] = { + BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 6), + BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, ARPOP_RREQUEST, 0, 1), + BPF_STMT(BPF_RET|BPF_K, 1024), + BPF_STMT(BPF_RET|BPF_K, 0), + }; + static struct sock_fprog filter = { + sizeof insns / sizeof(insns[0]), + insns + }; + + return setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)); +} + +int put_mylladdr(unsigned char **ptr_p, int ifindex, int alen) +{ + struct iflink *ifl; + + for (ifl=ifl_list; ifl; ifl = ifl->next) + if (ifl->index == ifindex) + break; + + if (ifl==NULL) + return -1; + + memcpy(*ptr_p, ifl->lladdr, alen); + *ptr_p += alen; + return 0; +} + +int put_myipaddr(unsigned char **ptr_p, int ifindex, __u32 hisipaddr) +{ + __u32 laddr = 0; + struct iflink *ifl; + struct ifaddr *ifa; + + for (ifl=ifl_list; ifl; ifl = ifl->next) + if (ifl->index == ifindex) + break; + + if (ifl==NULL) + return -1; + + for (ifa=ifl->ifa_list; ifa; ifa=ifa->next) { + if (!((ifa->prefix^hisipaddr)&ifa->mask)) { + laddr = ifa->local; + break; + } + } + memcpy(*ptr_p, &laddr, 4); + *ptr_p += 4; + return 0; +} + +void arp_advise(int ifindex, unsigned char *lladdr, int lllen, __u32 ipaddr) +{ + int fd; + struct arpreq req; + struct sockaddr_in *sin; + struct iflink *ifl; + + for (ifl=ifl_list; ifl; ifl = ifl->next) + if (ifl->index == ifindex) + break; + + if (ifl == NULL) + return; + + fd = socket(AF_INET, SOCK_DGRAM, 0); + memset(&req, 0, sizeof(req)); + req.arp_flags = ATF_COM; + sin = (struct sockaddr_in *)&req.arp_pa; + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = ipaddr; + req.arp_ha.sa_family = ifl->hatype; + memcpy(req.arp_ha.sa_data, lladdr, lllen); + memcpy(req.arp_dev, ifl->name, IFNAMSIZ); + + if (ioctl(fd, SIOCSARP, &req)) + syslog(LOG_ERR, "SIOCSARP: %m"); + close(fd); +} + +void serve_it(int fd) +{ + unsigned char buf[1024]; + struct sockaddr_ll sll; + socklen_t sll_len = sizeof(sll); + struct arphdr *a = (struct arphdr*)buf; + struct rarp_map *rmap; + unsigned char *ptr; + int n; + + n = recvfrom(fd, buf, sizeof(buf), MSG_DONTWAIT, (struct sockaddr*)&sll, &sll_len); + if (n<0) { + if (errno != EINTR && errno != EAGAIN) + syslog(LOG_ERR, "recvfrom: %m"); + return; + } + + /* Do not accept packets for other hosts and our own ones */ + if (sll.sll_pkttype != PACKET_BROADCAST && + sll.sll_pkttype != PACKET_MULTICAST && + sll.sll_pkttype != PACKET_HOST) + return; + + if (ifidx && sll.sll_ifindex != ifidx) + return; + + if (nar_op != htons(ARPOP_RREQUEST)) + return; + + if (verbose) { + int i; + char tmpbuf[16*3]; + char *ptr = tmpbuf; + for (i=0; i pln==4 */ + if (a->ar_pln != 4) { + syslog(LOG_ERR, "interesting rarp_req plen=%d", a->ar_pln); + return; + } + /* 2. ARP protocol must be IP */ + if (a->ar_pro != htons(ETH_P_IP)) { + syslog(LOG_ERR, "rarp protocol is not IP %04x", ntohs(a->ar_pro)); + return; + } + /* 3. ARP types must match */ + if (htons(sll.sll_hatype) != a->ar_hrd) { + switch (sll.sll_hatype) { + case ARPHRD_FDDI: + if (a->ar_hrd == htons(ARPHRD_ETHER) || + a->ar_hrd == htons(ARPHRD_IEEE802)) + break; + default: + syslog(LOG_ERR, "rarp htype mismatch"); + return; + } + } + /* 3. LL address lengths must be equal */ + if (a->ar_hln != sll.sll_halen) { + syslog(LOG_ERR, "rarp hlen mismatch"); + return; + } + /* 4. Check packet length */ + if (sizeof(*a) + 2*4 + 2*a->ar_hln > n) { + syslog(LOG_ERR, "truncated rarp request; len=%d", n); + return; + } + /* 5. Silly check: if this guy set different source + addresses in MAC header and in ARP, he is insane + */ + if (memcmp(sll.sll_addr, a+1, sll.sll_halen)) { + syslog(LOG_ERR, "this guy set different his lladdrs in arp and header"); + return; + } + /* End of sanity checks */ + + /* Lookup requested target in our database */ + rmap = rarp_lookup(sll.sll_ifindex, sll.sll_hatype, + sll.sll_halen, (unsigned char*)(a+1) + sll.sll_halen + 4); + if (rmap == NULL) + return; + + /* Prepare reply. It is almost ready, we only + replace ARP packet type, put our lladdr and + IP address to source fileds, + and fill target IP address. + */ + a->ar_op = htons(ARPOP_RREPLY); + ptr = (unsigned char*)(a+1); + if (put_mylladdr(&ptr, sll.sll_ifindex, rmap->lladdr_len)) + return; + if (put_myipaddr(&ptr, sll.sll_ifindex, rmap->ipaddr)) + return; + /* It is already filled */ + ptr += rmap->lladdr_len; + memcpy(ptr, &rmap->ipaddr, 4); + ptr += 4; + + /* Update our ARP cache. Probably, this guy + will not able to make ARP (if it is broken) + */ + arp_advise(sll.sll_ifindex, rmap->lladdr, rmap->lladdr_len, rmap->ipaddr); + + /* Sendto is blocking, but with 5sec timeout */ + alarm(5); + sendto(fd, buf, ptr - buf, 0, (struct sockaddr*)&sll, sizeof(sll)); + alarm(0); +} + +void catch_signal(int sig, void (*handler)(int)) +{ + struct sigaction sa; + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = handler; +#ifdef SA_INTERRUPT + sa.sa_flags = SA_INTERRUPT; +#endif + sigaction(sig, &sa, NULL); +} + +void sig_alarm(int signo) +{ +} + +void sig_hup(int signo) +{ + do_reload = 1; +} + +int main(int argc, char **argv) +{ + struct pollfd pset[2]; + int psize; + int opt; + + + opterr = 0; + while ((opt = getopt(argc, argv, "aAb:dvoe")) != EOF) { + switch (opt) { + case 'a': + ++all_ifaces; + break; + + case 'A': + ++listen_arp; + break; + + case 'd': + ++debug; + break; + + case 'v': + ++verbose; + break; + + case 'o': + ++allow_offlink; + break; + + case 'e': + ++only_ethers; + break; + + case 'b': + tftp_dir = optarg; + break; + + default: + usage(); + } + } + if (argc > optind) { + if (argc > optind+1) + usage(); + ifname = argv[optind]; + } + + psize = 1; + pset[0].fd = socket(PF_PACKET, SOCK_DGRAM, 0); + + if (ifname) { + struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, ifname, IFNAMSIZ); + if (ioctl(pset[0].fd, SIOCGIFINDEX, &ifr)) { + perror("ioctl(SIOCGIFINDEX)"); + usage(); + } + ifidx = ifr.ifr_ifindex; + } + + pset[1].fd = -1; + if (listen_arp) { + pset[1].fd = socket(PF_PACKET, SOCK_DGRAM, 0); + if (pset[1].fd >= 0) { + load_arp_bpflet(pset[1].fd); + psize = 1; + } + } + + if (pset[1].fd >= 0) { + struct sockaddr_ll sll; + memset(&sll, 0, sizeof(sll)); + sll.sll_family = AF_PACKET; + sll.sll_protocol = htons(ETH_P_ARP); + sll.sll_ifindex = all_ifaces ? 0 : ifidx; + if (bind(pset[1].fd, (struct sockaddr*)&sll, sizeof(sll)) < 0) { + close(pset[1].fd); + pset[1].fd = -1; + psize = 1; + } + } + if (pset[0].fd >= 0) { + struct sockaddr_ll sll; + memset(&sll, 0, sizeof(sll)); + sll.sll_family = AF_PACKET; + sll.sll_protocol = htons(ETH_P_RARP); + sll.sll_ifindex = all_ifaces ? 0 : ifidx; + if (bind(pset[0].fd, (struct sockaddr*)&sll, sizeof(sll)) < 0) { + close(pset[0].fd); + pset[0].fd = -1; + } + } + if (pset[0].fd < 0) { + pset[0] = pset[1]; + psize--; + } + if (psize == 0) { + fprintf(stderr, "failed to bind any socket. Aborting.\n"); + exit(1); + } + + if (!debug) { + int fd; + pid_t pid = fork(); + + if (pid > 0) + exit(0); + else if (pid == -1) { + perror("rarpd: fork"); + exit(1); + } + + if (chdir("/") < 0) { + perror("rarpd: chdir"); + exit(1); + } + + fd = open("/dev/null", O_RDWR); + if (fd >= 0) { + dup2(fd, 0); + dup2(fd, 1); + dup2(fd, 2); + if (fd > 2) + close(fd); + } + setsid(); + } + + openlog("rarpd", LOG_PID | LOG_CONS, LOG_DAEMON); + catch_signal(SIGALRM, sig_alarm); + catch_signal(SIGHUP, sig_hup); + + for (;;) { + int i; + + if (do_reload) { + configure(); + do_reload = 0; + } + +#define EVENTS (POLLIN|POLLPRI|POLLERR|POLLHUP) + pset[0].events = EVENTS; + pset[0].revents = 0; + pset[1].events = EVENTS; + pset[1].revents = 0; + + i = poll(pset, psize, -1); + if (i <= 0) { + if (errno != EINTR && i<0) { + syslog(LOG_ERR, "poll returned some crap: %m\n"); + sleep(10); + } + continue; + } + for (i=0; i +#include +#include +#include +#include +#include +#include +/* Do not use "improved" glibc version! */ +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +/* + * The next include contains all defs and structures for multicast + * that are not in SunOS 4.1.x. On a SunOS 4.1.x system none of this code + * is ever used because it does not support multicast + * Fraser Gardiner - Sun Microsystems Australia + */ + +#include +#include + +#include +#include + +#include "SNAPSHOT.h" + +struct interface +{ + struct in_addr address; /* Used to identify the interface */ + struct in_addr localaddr; /* Actual address if the interface */ + int preference; + int flags; + struct in_addr bcastaddr; + struct in_addr remoteaddr; + struct in_addr netmask; + int ifindex; + char name[IFNAMSIZ]; +}; + +/* + * TBD + * Use 255.255.255.255 for broadcasts - not the interface broadcast + * address. + */ + +#define ALLIGN(ptr) (ptr) + +static int join(int sock, struct sockaddr_in *sin); +static void solicitor(struct sockaddr_in *); +#ifdef RDISC_SERVER +static void advertise(struct sockaddr_in *, int lft); +#endif +static char *pr_name(struct in_addr addr); +static void pr_pack(char *buf, int cc, struct sockaddr_in *from); +static void age_table(int time); +static void record_router(struct in_addr router, int preference, int ttl); +static void add_route(struct in_addr addr); +static void del_route(struct in_addr addr); +static void rtioctl(struct in_addr addr, int op); +static int support_multicast(void); +static int sendbcast(int s, char *packet, int packetlen); +static int sendmcast(int s, char *packet, int packetlen, struct sockaddr_in *); +static int sendbcastif(int s, char *packet, int packetlen, struct interface *ifp); +static int sendmcastif(int s, char *packet, int packetlen, struct sockaddr_in *sin, struct interface *ifp); +static int is_directly_connected(struct in_addr in); +static void initlog(void); +static void discard_table(void); +static void init(void); + +#define ICMP_ROUTER_ADVERTISEMENT 9 +#define ICMP_ROUTER_SOLICITATION 10 + +#define ALL_HOSTS_ADDRESS "224.0.0.1" +#define ALL_ROUTERS_ADDRESS "224.0.0.2" + +#define MAXIFS 32 + +#if defined(__GLIBC__) && __GLIBC__ < 2 +/* For router advertisement */ +struct icmp_ra +{ + unsigned char icmp_type; /* type of message, see below */ + unsigned char icmp_code; /* type sub code */ + unsigned short icmp_cksum; /* ones complement cksum of struct */ + unsigned char icmp_num_addrs; + unsigned char icmp_wpa; /* Words per address */ + short icmp_lifetime; +}; + +struct icmp_ra_addr +{ + __u32 ira_addr; + __u32 ira_preference; +}; +#else +#define icmp_ra icmp +#endif + +/* Router constants */ +#define MAX_INITIAL_ADVERT_INTERVAL 16 +#define MAX_INITIAL_ADVERTISEMENTS 3 +#define MAX_RESPONSE_DELAY 2 /* Not used */ + +/* Host constants */ +#define MAX_SOLICITATIONS 3 +#define SOLICITATION_INTERVAL 3 +#define MAX_SOLICITATION_DELAY 1 /* Not used */ + +#define INELIGIBLE_PREF 0x80000000 /* Maximum negative */ + +#define MAX_ADV_INT 600 + +/* Statics */ +static int num_interfaces; + +static struct interface *interfaces; +static int interfaces_size; /* Number of elements in interfaces */ + + +#define MAXPACKET 4096 /* max packet size */ + +/* fraser */ +int debugfile; + +const char usage[] = +"Usage: rdisc [-b] [-d] [-s] [-v] [-f] [-a] [-V] [send_address] [receive_address]\n" +#ifdef RDISC_SERVER +" rdisc -r [-b] [-d] [-s] [-v] [-f] [-a] [-V] [-p ] [-T ]\n" +" [send_address] [receive_address]\n" +#endif +; + + +int s; /* Socket file descriptor */ +struct sockaddr_in whereto;/* Address to send to */ + +/* Common variables */ +int verbose = 0; +int debug = 0; +int trace = 0; +int solicit = 0; +int ntransmitted = 0; +int nreceived = 0; +int forever = 0; /* Never give up on host. If 0 defer fork until + * first response. + */ + +#ifdef RDISC_SERVER +/* Router variables */ +int responder; +int max_adv_int = MAX_ADV_INT; +int min_adv_int; +int lifetime; +int initial_advert_interval = MAX_INITIAL_ADVERT_INTERVAL; +int initial_advertisements = MAX_INITIAL_ADVERTISEMENTS; +int preference = 0; /* Setable with -p option */ +#endif + +/* Host variables */ +int max_solicitations = MAX_SOLICITATIONS; +unsigned int solicitation_interval = SOLICITATION_INTERVAL; +int best_preference = 1; /* Set to record only the router(s) with the + best preference in the kernel. Not set + puts all routes in the kernel. */ + + +static void graceful_finish(void); +static void finish(void); +static void timer(void); +static void initifs(void); +static unsigned short in_cksum(unsigned short *addr, int len); + +static int logging = 0; + +#define logerr(fmt...) ({ if (logging) syslog(LOG_ERR, fmt); \ + else fprintf(stderr, fmt); }) +#define logtrace(fmt...) ({ if (logging) syslog(LOG_INFO, fmt); \ + else fprintf(stderr, fmt); }) +#define logdebug(fmt...) ({ if (logging) syslog(LOG_DEBUG, fmt); \ + else fprintf(stderr, fmt); }) +static void logperror(char *str); + +static __inline__ int isbroadcast(struct sockaddr_in *sin) +{ + return (sin->sin_addr.s_addr == INADDR_BROADCAST); +} + +static __inline__ int ismulticast(struct sockaddr_in *sin) +{ + return IN_CLASSD(ntohl(sin->sin_addr.s_addr)); +} + +static void prusage(void) +{ + fputs(usage, stderr); + exit(1); +} + +void do_fork(void) +{ + int t; + pid_t pid; + long open_max; + + if (trace) + return; + if ((open_max = sysconf(_SC_OPEN_MAX)) == -1) { + if (errno == 0) { + (void) fprintf(stderr, "OPEN_MAX is not supported\n"); + } + else { + (void) fprintf(stderr, "sysconf() error\n"); + } + exit(1); + } + + + if ((pid=fork()) != 0) + exit(0); + + for (t = 0; t < open_max; t++) + if (t != s) + close(t); + + setsid(); + initlog(); +} + +void signal_setup(int signo, void (*handler)(void)) +{ + struct sigaction sa; + + memset(&sa, 0, sizeof(sa)); + + sa.sa_handler = (void (*)(int))handler; +#ifdef SA_INTERRUPT + sa.sa_flags = SA_INTERRUPT; +#endif + sigaction(signo, &sa, NULL); +} + +/* + * M A I N + */ +char *sendaddress, *recvaddress; + +int main(int argc, char **argv) +{ + struct sockaddr_in from; + char **av = argv; + struct sockaddr_in *to = &whereto; + struct sockaddr_in joinaddr; + sigset_t sset, sset_empty; +#ifdef RDISC_SERVER + int val; + + min_adv_int =( max_adv_int * 3 / 4); + lifetime = (3*max_adv_int); +#endif + + argc--, av++; + while (argc > 0 && *av[0] == '-') { + while (*++av[0]) { + switch (*av[0]) { + case 'd': + debug = 1; + break; + case 't': + trace = 1; + break; + case 'v': + verbose++; + break; + case 's': + solicit = 1; + break; +#ifdef RDISC_SERVER + case 'r': + responder = 1; + break; +#endif + case 'a': + best_preference = 0; + break; + case 'b': + best_preference = 1; + break; + case 'f': + forever = 1; + break; + case 'V': + printf("rdisc utility, iputils-%s\n", SNAPSHOT); + exit(0); +#ifdef RDISC_SERVER + case 'T': + argc--, av++; + if (argc != 0) { + val = strtol(av[0], (char **)NULL, 0); + if (val < 4 || val > 1800) { + (void) fprintf(stderr, + "Bad Max Advertizement Interval\n"); + exit(1); + } + max_adv_int = val; + min_adv_int =( max_adv_int * 3 / 4); + lifetime = (3*max_adv_int); + } else { + prusage(); + /* NOTREACHED*/ + } + goto next; + case 'p': + argc--, av++; + if (argc != 0) { + val = strtol(av[0], (char **)NULL, 0); + preference = val; + } else { + prusage(); + /* NOTREACHED*/ + } + goto next; +#endif + default: + prusage(); + /* NOTREACHED*/ + } + } +#ifdef RDISC_SERVER +next: +#endif + argc--, av++; + } + if( argc < 1) { + if (support_multicast()) { + sendaddress = ALL_ROUTERS_ADDRESS; +#ifdef RDISC_SERVER + if (responder) + sendaddress = ALL_HOSTS_ADDRESS; +#endif + } else + sendaddress = "255.255.255.255"; + } else { + sendaddress = av[0]; + argc--; + } + + if (argc < 1) { + if (support_multicast()) { + recvaddress = ALL_HOSTS_ADDRESS; +#ifdef RDISC_SERVER + if (responder) + recvaddress = ALL_ROUTERS_ADDRESS; +#endif + } else + recvaddress = "255.255.255.255"; + } else { + recvaddress = av[0]; + argc--; + } + if (argc != 0) { + (void) fprintf(stderr, "Extra parameters\n"); + prusage(); + /* NOTREACHED */ + } + +#ifdef RDISC_SERVER + if (solicit && responder) { + prusage(); + /* NOTREACHED */ + } +#endif + + if (!(solicit && !forever)) { + do_fork(); +/* + * Added the next line to stop forking a second time + * Fraser Gardiner - Sun Microsystems Australia + */ + forever = 1; + } + + memset( (char *)&whereto, 0, sizeof(struct sockaddr_in) ); + to->sin_family = AF_INET; + to->sin_addr.s_addr = inet_addr(sendaddress); + + memset( (char *)&joinaddr, 0, sizeof(struct sockaddr_in) ); + joinaddr.sin_family = AF_INET; + joinaddr.sin_addr.s_addr = inet_addr(recvaddress); + +#ifdef RDISC_SERVER + if (responder) + srandom((int)gethostid()); +#endif + + if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) { + logperror("socket"); + exit(5); + } + + setlinebuf( stdout ); + + signal_setup(SIGINT, finish ); + signal_setup(SIGTERM, graceful_finish ); + signal_setup(SIGHUP, initifs ); + signal_setup(SIGALRM, timer ); + + sigemptyset(&sset); + sigemptyset(&sset_empty); + sigaddset(&sset, SIGALRM); + sigaddset(&sset, SIGHUP); + sigaddset(&sset, SIGTERM); + sigaddset(&sset, SIGINT); + + init(); + if (join(s, &joinaddr) < 0) { + logerr("Failed joining addresses\n"); + exit (2); + } + + timer(); /* start things going */ + + for (;;) { + unsigned char packet[MAXPACKET]; + int len = sizeof (packet); + socklen_t fromlen = sizeof (from); + int cc; + + cc=recvfrom(s, (char *)packet, len, 0, + (struct sockaddr *)&from, &fromlen); + if (cc<0) { + if (errno == EINTR) + continue; + logperror("recvfrom"); + continue; + } + + sigprocmask(SIG_SETMASK, &sset, NULL); + pr_pack( (char *)packet, cc, &from ); + sigprocmask(SIG_SETMASK, &sset_empty, NULL); + } + /*NOTREACHED*/ +} + +#define TIMER_INTERVAL 3 +#define GETIFCONF_TIMER 30 + +static int left_until_advertise; + +/* Called every TIMER_INTERVAL */ +void timer() +{ + static int time; + static int left_until_getifconf; + static int left_until_solicit; + + + time += TIMER_INTERVAL; + + left_until_getifconf -= TIMER_INTERVAL; + left_until_advertise -= TIMER_INTERVAL; + left_until_solicit -= TIMER_INTERVAL; + + if (left_until_getifconf < 0) { + initifs(); + left_until_getifconf = GETIFCONF_TIMER; + } +#ifdef RDISC_SERVER + if (responder && left_until_advertise <= 0) { + ntransmitted++; + advertise(&whereto, lifetime); + if (ntransmitted < initial_advertisements) + left_until_advertise = initial_advert_interval; + else + left_until_advertise = min_adv_int + + ((max_adv_int - min_adv_int) * + (random() % 1000)/1000); + } else +#endif + if (solicit && left_until_solicit <= 0) { + ntransmitted++; + solicitor(&whereto); + if (ntransmitted < max_solicitations) + left_until_solicit = solicitation_interval; + else { + solicit = 0; + if (!forever && nreceived == 0) + exit(5); + } + } + age_table(TIMER_INTERVAL); + alarm(TIMER_INTERVAL); +} + +/* + * S O L I C I T O R + * + * Compose and transmit an ICMP ROUTER SOLICITATION REQUEST packet. + * The IP packet will be added on by the kernel. + */ +void +solicitor(struct sockaddr_in *sin) +{ + static unsigned char outpack[MAXPACKET]; + struct icmphdr *icp = (struct icmphdr *) ALLIGN(outpack); + int packetlen, i; + + if (verbose) { + logtrace("Sending solicitation to %s\n", + pr_name(sin->sin_addr)); + } + icp->type = ICMP_ROUTER_SOLICITATION; + icp->code = 0; + icp->checksum = 0; + icp->un.gateway = 0; /* Reserved */ + packetlen = 8; + + /* Compute ICMP checksum here */ + icp->checksum = in_cksum( (unsigned short *)icp, packetlen ); + + if (isbroadcast(sin)) + i = sendbcast(s, (char *)outpack, packetlen); + else if (ismulticast(sin)) + i = sendmcast(s, (char *)outpack, packetlen, sin); + else + i = sendto( s, (char *)outpack, packetlen, 0, + (struct sockaddr *)sin, sizeof(struct sockaddr)); + + if( i < 0 || i != packetlen ) { + if( i<0 ) { + logperror("solicitor:sendto"); + } + logerr("wrote %s %d chars, ret=%d\n", + sendaddress, packetlen, i ); + } +} + +#ifdef RDISC_SERVER +/* + * A V E R T I S E + * + * Compose and transmit an ICMP ROUTER ADVERTISEMENT packet. + * The IP packet will be added on by the kernel. + */ +void +advertise(struct sockaddr_in *sin, int lft) +{ + static unsigned char outpack[MAXPACKET]; + struct icmp_ra *rap = (struct icmp_ra *) ALLIGN(outpack); + struct icmp_ra_addr *ap; + int packetlen, i, cc; + + if (verbose) { + logtrace("Sending advertisement to %s\n", + pr_name(sin->sin_addr)); + } + + for (i = 0; i < num_interfaces; i++) { + rap->icmp_type = ICMP_ROUTER_ADVERTISEMENT; + rap->icmp_code = 0; + rap->icmp_cksum = 0; + rap->icmp_num_addrs = 0; + rap->icmp_wpa = 2; + rap->icmp_lifetime = htons(lft); + packetlen = 8; + + /* + * TODO handle multiple logical interfaces per + * physical interface. (increment with rap->icmp_wpa * 4 for + * each address.) + */ + ap = (struct icmp_ra_addr *)ALLIGN(outpack + ICMP_MINLEN); + ap->ira_addr = interfaces[i].localaddr.s_addr; + ap->ira_preference = htonl(interfaces[i].preference); + packetlen += rap->icmp_wpa * 4; + rap->icmp_num_addrs++; + + /* Compute ICMP checksum here */ + rap->icmp_cksum = in_cksum( (unsigned short *)rap, packetlen ); + + if (isbroadcast(sin)) + cc = sendbcastif(s, (char *)outpack, packetlen, + &interfaces[i]); + else if (ismulticast(sin)) + cc = sendmcastif( s, (char *)outpack, packetlen, sin, + &interfaces[i]); + else { + struct interface *ifp = &interfaces[i]; + /* + * Verify that the interface matches the destination + * address. + */ + if ((sin->sin_addr.s_addr & ifp->netmask.s_addr) == + (ifp->address.s_addr & ifp->netmask.s_addr)) { + if (debug) { + logdebug("Unicast to %s ", + pr_name(sin->sin_addr)); + logdebug("on interface %s, %s\n", + ifp->name, + pr_name(ifp->address)); + } + cc = sendto( s, (char *)outpack, packetlen, 0, + (struct sockaddr *)sin, + sizeof(struct sockaddr)); + } else + cc = packetlen; + } + if( cc < 0 || cc != packetlen ) { + if (cc < 0) { + logperror("sendto"); + } else { + logerr("wrote %s %d chars, ret=%d\n", + sendaddress, packetlen, cc ); + } + } + } +} +#endif + +/* + * P R _ T Y P E + * + * Convert an ICMP "type" field to a printable string. + */ +char * +pr_type(int t) +{ + static char *ttab[] = { + "Echo Reply", + "ICMP 1", + "ICMP 2", + "Dest Unreachable", + "Source Quench", + "Redirect", + "ICMP 6", + "ICMP 7", + "Echo", + "Router Advertise", + "Router Solicitation", + "Time Exceeded", + "Parameter Problem", + "Timestamp", + "Timestamp Reply", + "Info Request", + "Info Reply", + "Netmask Request", + "Netmask Reply" + }; + + if ( t < 0 || t > 16 ) + return("OUT-OF-RANGE"); + + return(ttab[t]); +} + +/* + * P R _ N A M E + * + * Return a string name for the given IP address. + */ +char *pr_name(struct in_addr addr) +{ + struct sockaddr_in sin = { .sin_family = AF_INET, .sin_addr = addr }; + char hnamebuf[NI_MAXHOST] = ""; + static char buf[80]; + + getnameinfo((struct sockaddr *) &sin, sizeof sin, hnamebuf, sizeof hnamebuf, NULL, 0, 0); + snprintf(buf, sizeof buf, "%s (%s)", hnamebuf, inet_ntoa(addr)); + return(buf); +} + +/* + * P R _ P A C K + * + * Print out the packet, if it came from us. This logic is necessary + * because ALL readers of the ICMP socket get a copy of ALL ICMP packets + * which arrive ('tis only fair). This permits multiple copies of this + * program to be run without having intermingled output (or statistics!). + */ +void +pr_pack(char *buf, int cc, struct sockaddr_in *from) +{ + struct iphdr *ip; + struct icmphdr *icp; + int i; + int hlen; + + ip = (struct iphdr *) ALLIGN(buf); + hlen = ip->ihl << 2; + if (cc < hlen + 8) { + if (verbose) + logtrace("packet too short (%d bytes) from %s\n", cc, + pr_name(from->sin_addr)); + return; + } + cc -= hlen; + icp = (struct icmphdr *)ALLIGN(buf + hlen); + + switch (icp->type) { + case ICMP_ROUTER_ADVERTISEMENT: + { + struct icmp_ra *rap = (struct icmp_ra *)ALLIGN(icp); + struct icmp_ra_addr *ap; + +#ifdef RDISC_SERVER + if (responder) + break; +#endif + + /* TBD verify that the link is multicast or broadcast */ + /* XXX Find out the link it came in over? */ + if (in_cksum((unsigned short *)ALLIGN(buf+hlen), cc)) { + if (verbose) + logtrace("ICMP %s from %s: Bad checksum\n", + pr_type((int)rap->icmp_type), + pr_name(from->sin_addr)); + return; + } + if (rap->icmp_code != 0) { + if (verbose) + logtrace("ICMP %s from %s: Code = %d\n", + pr_type((int)rap->icmp_type), + pr_name(from->sin_addr), + rap->icmp_code); + return; + } + if (rap->icmp_num_addrs < 1) { + if (verbose) + logtrace("ICMP %s from %s: No addresses\n", + pr_type((int)rap->icmp_type), + pr_name(from->sin_addr)); + return; + } + if (rap->icmp_wpa < 2) { + if (verbose) + logtrace("ICMP %s from %s: Words/addr = %d\n", + pr_type((int)rap->icmp_type), + pr_name(from->sin_addr), + rap->icmp_wpa); + return; + } + if ((unsigned)cc < + 8 + rap->icmp_num_addrs * rap->icmp_wpa * 4) { + if (verbose) + logtrace("ICMP %s from %s: Too short %d, %d\n", + pr_type((int)rap->icmp_type), + pr_name(from->sin_addr), + cc, + 8 + rap->icmp_num_addrs * rap->icmp_wpa * 4); + return; + } + + if (verbose) + logtrace("ICMP %s from %s, lifetime %d\n", + pr_type((int)rap->icmp_type), + pr_name(from->sin_addr), + ntohs(rap->icmp_lifetime)); + + /* Check that at least one router address is a neighboor + * on the arriving link. + */ + for (i = 0; (unsigned)i < rap->icmp_num_addrs; i++) { + struct in_addr ina; + ap = (struct icmp_ra_addr *) + ALLIGN(buf + hlen + 8 + + i * rap->icmp_wpa * 4); + ina.s_addr = ap->ira_addr; + if (verbose) + logtrace("\taddress %s, preference 0x%x\n", + pr_name(ina), + (unsigned int)ntohl(ap->ira_preference)); + if (is_directly_connected(ina)) + record_router(ina, + ntohl(ap->ira_preference), + ntohs(rap->icmp_lifetime)); + } + nreceived++; + if (!forever) { + do_fork(); + forever = 1; +/* + * The next line was added so that the alarm is set for the new procces + * Fraser Gardiner Sun Microsystems Australia + */ + (void) alarm(TIMER_INTERVAL); + } + break; + } + +#ifdef RDISC_SERVER + case ICMP_ROUTER_SOLICITATION: + { + struct sockaddr_in sin; + + if (!responder) + break; + + /* TBD verify that the link is multicast or broadcast */ + /* XXX Find out the link it came in over? */ + + if (in_cksum((unsigned short *)ALLIGN(buf+hlen), cc)) { + if (verbose) + logtrace("ICMP %s from %s: Bad checksum\n", + pr_type((int)icp->type), + pr_name(from->sin_addr)); + return; + } + if (icp->code != 0) { + if (verbose) + logtrace("ICMP %s from %s: Code = %d\n", + pr_type((int)icp->type), + pr_name(from->sin_addr), + icp->code); + return; + } + + if (cc < ICMP_MINLEN) { + if (verbose) + logtrace("ICMP %s from %s: Too short %d, %d\n", + pr_type((int)icp->type), + pr_name(from->sin_addr), + cc, + ICMP_MINLEN); + return; + } + + if (verbose) + logtrace("ICMP %s from %s\n", + pr_type((int)icp->type), + pr_name(from->sin_addr)); + + /* Check that ip_src is either a neighboor + * on the arriving link or 0. + */ + sin.sin_family = AF_INET; + if (ip->saddr == 0) { + /* If it was sent to the broadcast address we respond + * to the broadcast address. + */ + if (IN_CLASSD(ntohl(ip->daddr))) + sin.sin_addr.s_addr = htonl(0xe0000001); + else + sin.sin_addr.s_addr = INADDR_BROADCAST; + /* Restart the timer when we broadcast */ + left_until_advertise = min_adv_int + + ((max_adv_int - min_adv_int) + * (random() % 1000)/1000); + } else { + sin.sin_addr.s_addr = ip->saddr; + if (!is_directly_connected(sin.sin_addr)) { + if (verbose) + logtrace("ICMP %s from %s: source not directly connected\n", + pr_type((int)icp->type), + pr_name(from->sin_addr)); + break; + } + } + nreceived++; + ntransmitted++; + advertise(&sin, lifetime); + break; + } +#endif + } +} + + +/* + * I N _ C K S U M + * + * Checksum routine for Internet Protocol family headers (C Version) + * + */ +#if BYTE_ORDER == LITTLE_ENDIAN +# define ODDBYTE(v) (v) +#elif BYTE_ORDER == BIG_ENDIAN +# define ODDBYTE(v) ((unsigned short)(v) << 8) +#else +# define ODDBYTE(v) htons((unsigned short)(v) << 8) +#endif + +unsigned short in_cksum(unsigned short *addr, int len) +{ + register int nleft = len; + register unsigned short *w = addr; + register unsigned short answer; + register int sum = 0; + + /* + * Our algorithm is simple, using a 32 bit accumulator (sum), + * we add sequential 16 bit words to it, and at the end, fold + * back all the carry bits from the top 16 bits into the lower + * 16 bits. + */ + while( nleft > 1 ) { + sum += *w++; + nleft -= 2; + } + + /* mop up an odd byte, if necessary */ + if( nleft == 1 ) + sum += ODDBYTE(*(unsigned char *)w); /* le16toh() may be unavailable on old systems */ + + /* + * add back carry outs from top 16 bits to low 16 bits + */ + sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ + sum += (sum >> 16); /* add carry */ + answer = ~sum; /* truncate to 16 bits */ + return (answer); +} + +/* + * F I N I S H + * + * Print out statistics, and give up. + * Heavily buffered STDIO is used here, so that all the statistics + * will be written with 1 sys-write call. This is nice when more + * than one copy of the program is running on a terminal; it prevents + * the statistics output from becomming intermingled. + */ +void +finish() +{ +#ifdef RDISC_SERVER + if (responder) { + /* Send out a packet with a preference so that all + * hosts will know that we are dead. + * + * Wrong comment, wrong code. + * ttl must be set to 0 instead. --ANK + */ + logerr("terminated\n"); + ntransmitted++; + advertise(&whereto, 0); + } +#endif + logtrace("\n----%s rdisc Statistics----\n", sendaddress ); + logtrace("%d packets transmitted, ", ntransmitted ); + logtrace("%d packets received, ", nreceived ); + logtrace("\n"); + (void) fflush(stdout); + exit(0); +} + +void +graceful_finish() +{ + discard_table(); + finish(); + exit(0); +} + + +/* From libc/rpc/pmap_rmt.c */ + +int +sendbcast(int s, char *packet, int packetlen) +{ + int i, cc; + + for (i = 0; i < num_interfaces; i++) { + if ((interfaces[i].flags & (IFF_BROADCAST|IFF_POINTOPOINT)) == 0) + continue; + cc = sendbcastif(s, packet, packetlen, &interfaces[i]); + if (cc!= packetlen) { + return (cc); + } + } + return (packetlen); +} + +int +sendbcastif(int s, char *packet, int packetlen, struct interface *ifp) +{ + int on; + int cc; + struct sockaddr_in baddr; + + baddr.sin_family = AF_INET; + baddr.sin_addr = ifp->bcastaddr; + if (debug) + logdebug("Broadcast to %s\n", + pr_name(baddr.sin_addr)); + on = 1; + setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char*)&on, sizeof(on)); + cc = sendto(s, packet, packetlen, 0, + (struct sockaddr *)&baddr, sizeof (struct sockaddr)); + if (cc!= packetlen) { + logperror("sendbcast: sendto"); + logerr("Cannot send broadcast packet to %s\n", + pr_name(baddr.sin_addr)); + } + on = 0; + setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char*)&on, sizeof(on)); + return (cc); +} + +int +sendmcast(int s, char *packet, int packetlen, struct sockaddr_in *sin) +{ + int i, cc; + + for (i = 0; i < num_interfaces; i++) { + if ((interfaces[i].flags & (IFF_BROADCAST|IFF_POINTOPOINT|IFF_MULTICAST)) == 0) + continue; + cc = sendmcastif(s, packet, packetlen, sin, &interfaces[i]); + if (cc!= packetlen) { + return (cc); + } + } + return (packetlen); +} + +int +sendmcastif(int s, char *packet, int packetlen, struct sockaddr_in *sin, + struct interface *ifp) +{ + int cc; + struct ip_mreqn mreq; + + memset(&mreq, 0, sizeof(mreq)); + mreq.imr_ifindex = ifp->ifindex; + mreq.imr_address = ifp->localaddr; + if (debug) + logdebug("Multicast to interface %s, %s\n", + ifp->name, + pr_name(mreq.imr_address)); + if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, + (char *)&mreq, + sizeof(mreq)) < 0) { + logperror("setsockopt (IP_MULTICAST_IF)"); + logerr("Cannot send multicast packet over interface %s, %s\n", + ifp->name, + pr_name(mreq.imr_address)); + return (-1); + } + cc = sendto(s, packet, packetlen, 0, + (struct sockaddr *)sin, sizeof (struct sockaddr)); + if (cc!= packetlen) { + logperror("sendmcast: sendto"); + logerr("Cannot send multicast packet over interface %s, %s\n", + ifp->name, pr_name(mreq.imr_address)); + } + return (cc); +} + +void +init() +{ + initifs(); +#ifdef RDISC_SERVER + { + int i; + for (i = 0; i < interfaces_size; i++) + interfaces[i].preference = preference; + } +#endif +} + +void +initifs() +{ + int sock; + struct ifconf ifc; + struct ifreq ifreq, *ifr; + struct sockaddr_in *sin; + int n, i; + char *buf; + int numifs; + unsigned bufsize; + + sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock < 0) { + logperror("initifs: socket"); + return; + } +#ifdef SIOCGIFNUM + if (ioctl(sock, SIOCGIFNUM, (char *)&numifs) < 0) { + numifs = MAXIFS; + } +#else + numifs = MAXIFS; +#endif + bufsize = numifs * sizeof(struct ifreq); + buf = (char *)malloc(bufsize); + if (buf == NULL) { + logerr("out of memory\n"); + (void) close(sock); + return; + } + if (interfaces != NULL) + (void) free(interfaces); + interfaces = (struct interface *)ALLIGN(malloc(numifs * + sizeof(struct interface))); + if (interfaces == NULL) { + logerr("out of memory\n"); + (void) close(sock); + (void) free(buf); + return; + } + interfaces_size = numifs; + + ifc.ifc_len = bufsize; + ifc.ifc_buf = buf; + if (ioctl(sock, SIOCGIFCONF, (char *)&ifc) < 0) { + logperror("initifs: ioctl (get interface configuration)"); + (void) close(sock); + (void) free(buf); + return; + } + ifr = ifc.ifc_req; + for (i = 0, n = ifc.ifc_len/sizeof (struct ifreq); n > 0; n--, ifr++) { + ifreq = *ifr; + if (strlen(ifreq.ifr_name) >= IFNAMSIZ) + continue; + if (ioctl(sock, SIOCGIFFLAGS, (char *)&ifreq) < 0) { + logperror("initifs: ioctl (get interface flags)"); + continue; + } + if (ifr->ifr_addr.sa_family != AF_INET) + continue; + if ((ifreq.ifr_flags & IFF_UP) == 0) + continue; + if (ifreq.ifr_flags & IFF_LOOPBACK) + continue; + if ((ifreq.ifr_flags & (IFF_MULTICAST|IFF_BROADCAST|IFF_POINTOPOINT)) == 0) + continue; + strncpy(interfaces[i].name, ifr->ifr_name, IFNAMSIZ-1); + + sin = (struct sockaddr_in *)ALLIGN(&ifr->ifr_addr); + interfaces[i].localaddr = sin->sin_addr; + interfaces[i].flags = ifreq.ifr_flags; + interfaces[i].netmask.s_addr = (__u32)0xffffffff; + if (ioctl(sock, SIOCGIFINDEX, (char *)&ifreq) < 0) { + logperror("initifs: ioctl (get ifindex)"); + continue; + } + interfaces[i].ifindex = ifreq.ifr_ifindex; + if (ifreq.ifr_flags & IFF_POINTOPOINT) { + if (ioctl(sock, SIOCGIFDSTADDR, (char *)&ifreq) < 0) { + logperror("initifs: ioctl (get destination addr)"); + continue; + } + sin = (struct sockaddr_in *)ALLIGN(&ifreq.ifr_addr); + /* A pt-pt link is identified by the remote address */ + interfaces[i].address = sin->sin_addr; + interfaces[i].remoteaddr = sin->sin_addr; + /* Simulate broadcast for pt-pt */ + interfaces[i].bcastaddr = sin->sin_addr; + interfaces[i].flags |= IFF_BROADCAST; + } else { + /* Non pt-pt links are identified by the local address */ + interfaces[i].address = interfaces[i].localaddr; + interfaces[i].remoteaddr = interfaces[i].address; + if (ioctl(sock, SIOCGIFNETMASK, (char *)&ifreq) < 0) { + logperror("initifs: ioctl (get netmask)"); + continue; + } + sin = (struct sockaddr_in *)ALLIGN(&ifreq.ifr_addr); + interfaces[i].netmask = sin->sin_addr; + if (ifreq.ifr_flags & IFF_BROADCAST) { + if (ioctl(sock, SIOCGIFBRDADDR, (char *)&ifreq) < 0) { + logperror("initifs: ioctl (get broadcast address)"); + continue; + } + sin = (struct sockaddr_in *)ALLIGN(&ifreq.ifr_addr); + interfaces[i].bcastaddr = sin->sin_addr; + } + } +#ifdef notdef + if (debug) + logdebug("Found interface %s, flags 0x%x\n", + pr_name(interfaces[i].localaddr), + interfaces[i].flags); +#endif + i++; + } + num_interfaces = i; +#ifdef notdef + if (debug) + logdebug("Found %d interfaces\n", num_interfaces); +#endif + (void) close(sock); + (void) free(buf); +} + +int +join(int sock, struct sockaddr_in *sin) +{ + int i, j; + struct ip_mreqn mreq; + int joined[num_interfaces]; + + memset(joined, 0, sizeof(joined)); + + if (isbroadcast(sin)) + return (0); + + mreq.imr_multiaddr = sin->sin_addr; + for (i = 0; i < num_interfaces; i++) { + for (j = 0; j < i; j++) { + if (joined[j] == interfaces[i].ifindex) + break; + } + if (j != i) + continue; + + mreq.imr_ifindex = interfaces[i].ifindex; + mreq.imr_address.s_addr = 0; + + if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, + (char *)&mreq, sizeof(mreq)) < 0) { + logperror("setsockopt (IP_ADD_MEMBERSHIP)"); + return (-1); + } + + joined[i] = interfaces[i].ifindex; + } + return (0); +} + +int support_multicast() +{ + int sock; + unsigned char ttl = 1; + + sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sock < 0) { + logperror("support_multicast: socket"); + return (0); + } + + if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, + (char *)&ttl, sizeof(ttl)) < 0) { + (void) close(sock); + return (0); + } + (void) close(sock); + return (1); +} + +int +is_directly_connected(struct in_addr in) +{ + int i; + + for (i = 0; i < num_interfaces; i++) { + /* Check that the subnetwork numbers match */ + + if ((in.s_addr & interfaces[i].netmask.s_addr ) == + (interfaces[i].remoteaddr.s_addr & interfaces[i].netmask.s_addr)) + return (1); + } + return (0); +} + +/* + * TABLES + */ +struct table { + struct in_addr router; + int preference; + int remaining_time; + int in_kernel; + struct table *next; +}; + +struct table *table; + +struct table * +find_router(struct in_addr addr) +{ + struct table *tp; + + tp = table; + while (tp) { + if (tp->router.s_addr == addr.s_addr) + return (tp); + tp = tp->next; + } + return (NULL); +} + +int max_preference(void) +{ + struct table *tp; + int max = (int)INELIGIBLE_PREF; + + tp = table; + while (tp) { + if (tp->preference > max) + max = tp->preference; + tp = tp->next; + } + return (max); +} + + +/* Note: this might leave the kernel with no default route for a short time. */ +void +age_table(int time) +{ + struct table **tpp, *tp; + int recalculate_max = 0; + int max = max_preference(); + + tpp = &table; + while (*tpp != NULL) { + tp = *tpp; + tp->remaining_time -= time; + if (tp->remaining_time <= 0) { + *tpp = tp->next; + if (tp->in_kernel) + del_route(tp->router); + if (best_preference && + tp->preference == max) + recalculate_max++; + free((char *)tp); + } else { + tpp = &tp->next; + } + } + if (recalculate_max) { + int max = max_preference(); + + if (max != INELIGIBLE_PREF) { + tp = table; + while (tp) { + if (tp->preference == max && !tp->in_kernel) { + add_route(tp->router); + tp->in_kernel++; + } + tp = tp->next; + } + } + } +} + +void discard_table(void) +{ + struct table **tpp, *tp; + + tpp = &table; + while (*tpp != NULL) { + tp = *tpp; + *tpp = tp->next; + if (tp->in_kernel) + del_route(tp->router); + free((char *)tp); + } +} + + +void +record_router(struct in_addr router, int preference, int ttl) +{ + struct table *tp; + int old_max = max_preference(); + int changed_up = 0; /* max preference could have increased */ + int changed_down = 0; /* max preference could have decreased */ + + if (ttl < 4) + preference = INELIGIBLE_PREF; + + if (debug) + logdebug("Recording %s, ttl %d, preference 0x%x\n", + pr_name(router), + ttl, + preference); + tp = find_router(router); + if (tp) { + if (tp->preference > preference && + tp->preference == old_max) + changed_down++; + else if (preference > tp->preference) + changed_up++; + tp->preference = preference; + tp->remaining_time = ttl; + } else { + if (preference > old_max) + changed_up++; + tp = (struct table *)ALLIGN(malloc(sizeof(struct table))); + if (tp == NULL) { + logerr("Out of memory\n"); + return; + } + tp->router = router; + tp->preference = preference; + tp->remaining_time = ttl; + tp->in_kernel = 0; + tp->next = table; + table = tp; + } + if (!tp->in_kernel && + (!best_preference || tp->preference == max_preference()) && + tp->preference != INELIGIBLE_PREF) { + add_route(tp->router); + tp->in_kernel++; + } + if (tp->preference == INELIGIBLE_PREF && tp->in_kernel) { + del_route(tp->router); + tp->in_kernel = 0; + } + if (best_preference && changed_down) { + /* Check if we should add routes */ + int new_max = max_preference(); + if (new_max != INELIGIBLE_PREF) { + tp = table; + while (tp) { + if (tp->preference == new_max && + !tp->in_kernel) { + add_route(tp->router); + tp->in_kernel++; + } + tp = tp->next; + } + } + } + if (best_preference && (changed_up || changed_down)) { + /* Check if we should remove routes already in the kernel */ + int new_max = max_preference(); + tp = table; + while (tp) { + if (tp->preference < new_max && tp->in_kernel) { + del_route(tp->router); + tp->in_kernel = 0; + } + tp = tp->next; + } + } +} + +void +add_route(struct in_addr addr) +{ + if (debug) + logdebug("Add default route to %s\n", pr_name(addr)); + rtioctl(addr, SIOCADDRT); +} + +void +del_route(struct in_addr addr) +{ + if (debug) + logdebug("Delete default route to %s\n", pr_name(addr)); + rtioctl(addr, SIOCDELRT); +} + +void +rtioctl(struct in_addr addr, int op) +{ + int sock; + struct rtentry rt; + struct sockaddr_in *sin; + + memset((char *)&rt, 0, sizeof(struct rtentry)); + rt.rt_dst.sa_family = AF_INET; + rt.rt_gateway.sa_family = AF_INET; + rt.rt_genmask.sa_family = AF_INET; + sin = (struct sockaddr_in *)ALLIGN(&rt.rt_gateway); + sin->sin_addr = addr; + rt.rt_flags = RTF_UP | RTF_GATEWAY; + + sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sock < 0) { + logperror("rtioctl: socket"); + return; + } + if (ioctl(sock, op, (char *)&rt) < 0) { + if (!(op == SIOCADDRT && errno == EEXIST)) + logperror("ioctl (add/delete route)"); + } + (void) close(sock); +} + +/* + * LOGGER + */ + +void initlog(void) +{ + logging++; + openlog("in.rdiscd", LOG_PID | LOG_CONS, LOG_DAEMON); +} + + +void +logperror(char *str) +{ + if (logging) + syslog(LOG_ERR, "%s: %m", str); + else + (void) fprintf(stderr, "%s: %s\n", str, strerror(errno)); +} diff --git a/tftp.h b/tftp.h new file mode 100644 index 0000000..db9a6df --- /dev/null +++ b/tftp.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#define PKTSIZE SEGSIZE+4 + +#ifndef _ARPA_TFTP_H +#define _ARPA_TFTP_H +/* + * Trivial File Transfer Protocol (IEN-133) + */ +#define SEGSIZE 512 /* data segment size */ + +/* + * Packet types. + */ +#define RRQ 01 /* read request */ +#define WRQ 02 /* write request */ +#define DATA 03 /* data packet */ +#define ACK 04 /* acknowledgement */ +#define ERROR 05 /* error code */ + +struct tftphdr { + short th_opcode; /* packet type */ + union { + short tu_block; /* block # */ + short tu_code; /* error code */ + char tu_stuff[1]; /* request packet stuff */ + } th_u; + char th_data[1]; /* data or error string */ +}; + +#define th_block th_u.tu_block +#define th_code th_u.tu_code +#define th_stuff th_u.tu_stuff +#define th_msg th_data + +/* + * Error codes. + */ +#define EUNDEF 0 /* not defined */ +#define ENOTFOUND 1 /* file not found */ +#define EACCESS 2 /* access violation */ +#define ENOSPACE 3 /* disk full or allocation exceeded */ +#define EBADOP 4 /* illegal TFTP operation */ +#define EBADID 5 /* unknown transfer ID */ +#define EEXISTS 6 /* file already exists */ +#define ENOUSER 7 /* no such user */ + + +extern int readit(FILE * file, struct tftphdr **dpp, int convert); +extern void read_ahead(FILE *file, int convert); +extern int writeit(FILE *file, struct tftphdr **dpp, int ct, int convert); +extern int write_behind(FILE *file, int convert); +extern int synchnet(int f); +extern struct tftphdr *w_init(void); +extern struct tftphdr *r_init(void); + + +#endif /* _ARPA_TFTP_H */ diff --git a/tftpd.c b/tftpd.c new file mode 100644 index 0000000..3a03eea --- /dev/null +++ b/tftpd.c @@ -0,0 +1,520 @@ +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * Trivial file transfer protocol server. + * + * This version includes many modifications by Jim Guyton + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tftp.h" + +#define TIMEOUT 5 + +int peer; +int rexmtval = TIMEOUT; +int maxtimeout = 5*TIMEOUT; +char buf[PKTSIZE]; +char ackbuf[PKTSIZE]; +union { + struct sockaddr sa; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; +} from; +socklen_t fromlen; + +#define MAXARG 1 +char *dirs[MAXARG+1]; + +void tftp(struct tftphdr *tp, int size) __attribute__((noreturn)); +void nak(int error); +int validate_access(char *filename, int mode); + +struct formats; + +void sendfile(struct formats *pf); +void recvfile(struct formats *pf); + + +int main(int ac, char **av) +{ + register struct tftphdr *tp; + register int n = 0; + int on = 1; + + openlog("tftpd", LOG_PID, LOG_DAEMON); + + /* Sanity. If parent forgot to setuid() on us. */ + if (geteuid() == 0) { + /* Drop all supplementary groups. No error checking is needed */ + setgroups(0, NULL); + if (setgid(65534) || setuid(65534)) { + syslog(LOG_ERR, "set*id failed: %m\n"); + exit(1); + } + } + + ac--; av++; + while (ac-- > 0 && n < MAXARG) + dirs[n++] = *av++; + + if (ioctl(0, FIONBIO, &on) < 0) { + syslog(LOG_ERR, "ioctl(FIONBIO): %m\n"); + exit(1); + } + fromlen = sizeof (from); + n = recvfrom(0, buf, sizeof (buf), 0, + (struct sockaddr *)&from, &fromlen); + if (n < 0) { + if (errno != EAGAIN) + syslog(LOG_ERR, "recvfrom: %m\n"); + exit(1); + } + /* + * Now that we have read the message out of the UDP + * socket, we fork and exit. Thus, inetd will go back + * to listening to the tftp port, and the next request + * to come in will start up a new instance of tftpd. + * + * We do this so that inetd can run tftpd in "wait" mode. + * The problem with tftpd running in "nowait" mode is that + * inetd may get one or more successful "selects" on the + * tftp port before we do our receive, so more than one + * instance of tftpd may be started up. Worse, if tftpd + * break before doing the above "recvfrom", inetd would + * spawn endless instances, clogging the system. + */ + { + int pid; + int i; + socklen_t j; + + for (i = 1; i < 20; i++) { + pid = fork(); + if (pid < 0) { + sleep(i); + /* + * flush out to most recently sent request. + * + * This may drop some request, but those + * will be resent by the clients when + * they timeout. The positive effect of + * this flush is to (try to) prevent more + * than one tftpd being started up to service + * a single request from a single client. + */ + j = sizeof from; + i = recvfrom(0, buf, sizeof (buf), 0, + (struct sockaddr *)&from, &j); + if (i > 0) { + n = i; + fromlen = j; + } + } else { + break; + } + } + if (pid < 0) { + syslog(LOG_ERR, "fork: %m\n"); + exit(1); + } else if (pid != 0) { + exit(0); + } + } + alarm(0); + close(0); + close(1); + peer = socket(from.sa.sa_family, SOCK_DGRAM, 0); + if (peer < 0) { + syslog(LOG_ERR, "socket: %m\n"); + exit(1); + } + if (connect(peer, (struct sockaddr *)&from, sizeof(from)) < 0) { + syslog(LOG_ERR, "connect: %m\n"); + exit(1); + } + tp = (struct tftphdr *)buf; + tp->th_opcode = ntohs(tp->th_opcode); + if (tp->th_opcode == RRQ || tp->th_opcode == WRQ) + tftp(tp, n); + exit(1); +} + +struct formats { + char *f_mode; + int (*f_validate)(char *filename, int mode); + void (*f_send)(struct formats*); + void (*f_recv)(struct formats*); + int f_convert; +} formats[] = { + { "netascii", validate_access, sendfile, recvfile, 1 }, + { "octet", validate_access, sendfile, recvfile, 0 }, +#ifdef notdef + { "mail", validate_user, sendmail, recvmail, 1 }, +#endif + { 0 } +}; + +/* + * Handle initial connection protocol. + */ +void tftp(struct tftphdr *tp, int size) +{ + register char *cp; + int first = 1, ecode; + register struct formats *pf; + char *filename, *mode = NULL; + + filename = cp = tp->th_stuff; +again: + while (cp < buf + size) { + if (*cp == '\0') + break; + cp++; + } + if (*cp != '\0') { + nak(EBADOP); + exit(1); + } + if (first) { + mode = ++cp; + first = 0; + goto again; + } + for (cp = mode; *cp; cp++) + if (isupper(*cp)) + *cp = tolower(*cp); + for (pf = formats; pf->f_mode; pf++) + if (strcmp(pf->f_mode, mode) == 0) + break; + if (pf->f_mode == 0) { + nak(EBADOP); + exit(1); + } + ecode = (*pf->f_validate)(filename, tp->th_opcode); + if (ecode) { + nak(ecode); + exit(1); + } + if (tp->th_opcode == WRQ) + (*pf->f_recv)(pf); + else + (*pf->f_send)(pf); + exit(0); +} + + +FILE *file; + +/* + * Validate file access. Since we + * have no uid or gid, for now require + * file to exist and be publicly + * readable/writable. + * If we were invoked with arguments + * from inetd then the file must also be + * in one of the given directory prefixes. + * Note also, full path name must be + * given as we have no login directory. + */ +int validate_access(char *filename, int mode) +{ + struct stat stbuf; + int fd; + char *cp; + char fnamebuf[1024+512]; + + for (cp = filename; *cp; cp++) { + if(*cp == '.' && (cp == filename || strncmp(cp-1, "/../", 4) == 0)) { + syslog(LOG_ERR, "bad path %s", filename); + return(EACCESS); + } + } + + if (*filename == '/') + filename++; + + if (!*dirs) { + syslog(LOG_ERR, "no dirs"); + return EACCESS; + } + snprintf(fnamebuf, sizeof(fnamebuf)-1, "%s/%s", *dirs, filename); + filename = fnamebuf; + + if (stat(filename, &stbuf) < 0) { + syslog(LOG_ERR, "stat %s : %m", filename); + return (errno == ENOENT ? ENOTFOUND : EACCESS); + } + if (mode == RRQ) { + if ((stbuf.st_mode&(S_IREAD >> 6)) == 0) { + syslog(LOG_ERR, "not readable %s", filename); + return (EACCESS); + } + } else { + if ((stbuf.st_mode&(S_IWRITE >> 6)) == 0) { + syslog(LOG_ERR, "not writable %s", filename); + return (EACCESS); + } + } + fd = open(filename, mode == RRQ ? 0 : 1); + if (fd < 0) { + syslog(LOG_ERR, "cannot open %s: %m", filename); + return (errno + 100); + } + file = fdopen(fd, (mode == RRQ)? "r":"w"); + if (file == NULL) { + return errno+100; + } + return (0); +} + +int confirmed; +int timeout; +jmp_buf timeoutbuf; + +void timer(int signo) +{ + confirmed = 0; + timeout += rexmtval; + if (timeout >= maxtimeout) + exit(1); + longjmp(timeoutbuf, 1); +} + +/* + * Send the requested file. + */ +void sendfile(struct formats *pf) +{ + struct tftphdr *dp; + register struct tftphdr *ap; /* ack packet */ + volatile int block = 1; + int size, n; + + confirmed = 0; + signal(SIGALRM, timer); + dp = r_init(); + ap = (struct tftphdr *)ackbuf; + do { + size = readit(file, &dp, pf->f_convert); + if (size < 0) { + nak(errno + 100); + goto abort; + } + dp->th_opcode = htons((unsigned short)DATA); + dp->th_block = htons((unsigned short)block); + timeout = 0; + (void) setjmp(timeoutbuf); + +send_data: + if (send(peer, dp, size + 4, confirmed) != size + 4) { + syslog(LOG_ERR, "tftpd: write: %m\n"); + goto abort; + } + confirmed = 0; + read_ahead(file, pf->f_convert); + for ( ; ; ) { + alarm(rexmtval); /* read the ack */ + n = recv(peer, ackbuf, sizeof (ackbuf), 0); + alarm(0); + if (n < 0) { + syslog(LOG_ERR, "tftpd: read: %m\n"); + goto abort; + } + ap->th_opcode = ntohs((unsigned short)ap->th_opcode); + ap->th_block = ntohs((unsigned short)ap->th_block); + + if (ap->th_opcode == ERROR) + goto abort; + + if (ap->th_opcode == ACK) { + if (ap->th_block == block) { + confirmed = MSG_CONFIRM; + break; + } + /* Re-synchronize with the other side */ + synchnet(peer); + if (ap->th_block == (block -1)) { + goto send_data; + } + } + + } + block++; + } while (size == SEGSIZE); +abort: + (void) fclose(file); +} + +void justquit(int signo) +{ + exit(0); +} + + +/* + * Receive a file. + */ +void recvfile(struct formats *pf) +{ + struct tftphdr *dp; + register struct tftphdr *ap; /* ack buffer */ + volatile int block = 0, n, size; + + confirmed = 0; + signal(SIGALRM, timer); + dp = w_init(); + ap = (struct tftphdr *)ackbuf; + do { + timeout = 0; + ap->th_opcode = htons((unsigned short)ACK); + ap->th_block = htons((unsigned short)block); + block++; + (void) setjmp(timeoutbuf); +send_ack: + if (send(peer, ackbuf, 4, confirmed) != 4) { + syslog(LOG_ERR, "tftpd: write: %m\n"); + goto abort; + } + confirmed = 0; + write_behind(file, pf->f_convert); + for ( ; ; ) { + alarm(rexmtval); + n = recv(peer, dp, PKTSIZE, 0); + alarm(0); + if (n < 0) { /* really? */ + syslog(LOG_ERR, "tftpd: read: %m\n"); + goto abort; + } + dp->th_opcode = ntohs((unsigned short)dp->th_opcode); + dp->th_block = ntohs((unsigned short)dp->th_block); + if (dp->th_opcode == ERROR) + goto abort; + if (dp->th_opcode == DATA) { + if (dp->th_block == block) { + confirmed = MSG_CONFIRM; + break; /* normal */ + } + /* Re-synchronize with the other side */ + (void) synchnet(peer); + if (dp->th_block == (block-1)) + goto send_ack; /* rexmit */ + } + } + /* size = write(file, dp->th_data, n - 4); */ + size = writeit(file, &dp, n - 4, pf->f_convert); + if (size != (n-4)) { /* ahem */ + if (size < 0) nak(errno + 100); + else nak(ENOSPACE); + goto abort; + } + } while (size == SEGSIZE); + write_behind(file, pf->f_convert); + (void) fclose(file); /* close data file */ + + ap->th_opcode = htons((unsigned short)ACK); /* send the "final" ack */ + ap->th_block = htons((unsigned short)(block)); + (void) send(peer, ackbuf, 4, confirmed); + + signal(SIGALRM, justquit); /* just quit on timeout */ + alarm(rexmtval); + n = recv(peer, buf, sizeof (buf), 0); /* normally times out and quits */ + alarm(0); + if (n >= 4 && /* if read some data */ + dp->th_opcode == DATA && /* and got a data block */ + block == dp->th_block) { /* then my last ack was lost */ + (void) send(peer, ackbuf, 4, 0); /* resend final ack */ + } +abort: + return; +} + +struct errmsg { + int e_code; + char *e_msg; +} errmsgs[] = { + { EUNDEF, "Undefined error code" }, + { ENOTFOUND, "File not found" }, + { EACCESS, "Access violation" }, + { ENOSPACE, "Disk full or allocation exceeded" }, + { EBADOP, "Illegal TFTP operation" }, + { EBADID, "Unknown transfer ID" }, + { EEXISTS, "File already exists" }, + { ENOUSER, "No such user" }, + { -1, 0 } +}; + +/* + * Send a nak packet (error message). + * Error code passed in is one of the + * standard TFTP codes, or a UNIX errno + * offset by 100. + */ +void nak(int error) +{ + register struct tftphdr *tp; + int length; + register struct errmsg *pe; + + tp = (struct tftphdr *)buf; + tp->th_opcode = htons((unsigned short)ERROR); + tp->th_code = htons((unsigned short)error); + for (pe = errmsgs; pe->e_code >= 0; pe++) + if (pe->e_code == error) + break; + if (pe->e_code < 0) { + pe->e_msg = strerror(error - 100); + tp->th_code = EUNDEF; /* set 'undef' errorcode */ + } + strcpy(tp->th_msg, pe->e_msg); + length = strlen(pe->e_msg); + tp->th_msg[length] = '\0'; + length += 5; + if (send(peer, buf, length, 0) != length) + syslog(LOG_ERR, "nak: %m\n"); +} diff --git a/tftpsubs.c b/tftpsubs.c new file mode 100644 index 0000000..6b1ccb6 --- /dev/null +++ b/tftpsubs.c @@ -0,0 +1,239 @@ +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* Simple minded read-ahead/write-behind subroutines for tftp user and + server. Written originally with multiple buffers in mind, but current + implementation has two buffer logic wired in. + + Todo: add some sort of final error check so when the write-buffer + is finally flushed, the caller can detect if the disk filled up + (or had an i/o error) and return a nak to the other side. + + Jim Guyton 10/85 + */ + +#include +#include +#include +#include +#include +#include + +#include "tftp.h" + +struct bf { + int counter; /* size of data in buffer, or flag */ + char buf[PKTSIZE]; /* room for data packet */ +} bfs[2]; + + /* Values for bf.counter */ +#define BF_ALLOC -3 /* alloc'd but not yet filled */ +#define BF_FREE -2 /* free */ +/* [-1 .. SEGSIZE] = size of data in the data buffer */ + +static int nextone; /* index of next buffer to use */ +static int current; /* index of buffer in use */ + + /* control flags for crlf conversions */ +int newline = 0; /* fillbuf: in middle of newline expansion */ +int prevchar = -1; /* putbuf: previous char (cr check) */ + +struct tftphdr *rw_init(int); + +struct tftphdr *w_init() { return rw_init(0); } /* write-behind */ +struct tftphdr *r_init() { return rw_init(1); } /* read-ahead */ + +/* init for either read-ahead or write-behind */ +/* x is zero for write-behind, one for read-head */ +struct tftphdr *rw_init(int x) +{ + newline = 0; /* init crlf flag */ + prevchar = -1; + bfs[0].counter = BF_ALLOC; /* pass out the first buffer */ + current = 0; + bfs[1].counter = BF_FREE; + nextone = x; /* ahead or behind? */ + return (struct tftphdr *)bfs[0].buf; +} + + +/* Have emptied current buffer by sending to net and getting ack. + Free it and return next buffer filled with data. + */ +int readit(FILE * file, struct tftphdr **dpp, int convert) +{ + struct bf *b; + + bfs[current].counter = BF_FREE; /* free old one */ + current = !current; /* "incr" current */ + + b = &bfs[current]; /* look at new buffer */ + if (b->counter == BF_FREE) /* if it's empty */ + read_ahead(file, convert); /* fill it */ +#if 0 + assert(b->counter != BF_FREE); /* check */ +#endif + *dpp = (struct tftphdr *)b->buf; /* set caller's ptr */ + return b->counter; +} + +/* + * fill the input buffer, doing ascii conversions if requested + * conversions are lf -> cr,lf and cr -> cr, nul + */ +void read_ahead(FILE *file, int convert) +{ + register int i; + register char *p; + register int c; + struct bf *b; + struct tftphdr *dp; + + b = &bfs[nextone]; /* look at "next" buffer */ + if (b->counter != BF_FREE) /* nop if not free */ + return; + nextone = !nextone; /* "incr" next buffer ptr */ + + dp = (struct tftphdr *)b->buf; + + if (convert == 0) { + b->counter = read(fileno(file), dp->th_data, SEGSIZE); + return; + } + + p = dp->th_data; + for (i = 0 ; i < SEGSIZE; i++) { + if (newline) { + if (prevchar == '\n') + c = '\n'; /* lf to cr,lf */ + else c = '\0'; /* cr to cr,nul */ + newline = 0; + } + else { + c = getc(file); + if (c == EOF) break; + if (c == '\n' || c == '\r') { + prevchar = c; + c = '\r'; + newline = 1; + } + } + *p++ = c; + } + b->counter = (int)(p - dp->th_data); +} + +/* Update count associated with the buffer, get new buffer + from the queue. Calls write_behind only if next buffer not + available. + */ +int writeit(FILE *file, struct tftphdr **dpp, int ct, int convert) +{ + bfs[current].counter = ct; /* set size of data to write */ + current = !current; /* switch to other buffer */ + if (bfs[current].counter != BF_FREE) /* if not free */ + write_behind(file, convert); /* flush it */ + bfs[current].counter = BF_ALLOC; /* mark as alloc'd */ + *dpp = (struct tftphdr *)bfs[current].buf; + return ct; /* this is a lie of course */ +} + +/* + * Output a buffer to a file, converting from netascii if requested. + * CR,NUL -> CR and CR,LF => LF. + * Note spec is undefined if we get CR as last byte of file or a + * CR followed by anything else. In this case we leave it alone. + */ +int write_behind(FILE *file, int convert) +{ + char *buf; + int count; + register int ct; + register char *p; + register int c; /* current character */ + struct bf *b; + struct tftphdr *dp; + + b = &bfs[nextone]; + if (b->counter < -1) /* anything to flush? */ + return 0; /* just nop if nothing to do */ + + count = b->counter; /* remember byte count */ + b->counter = BF_FREE; /* reset flag */ + dp = (struct tftphdr *)b->buf; + nextone = !nextone; /* incr for next time */ + buf = dp->th_data; + + if (count <= 0) return -1; /* nak logic? */ + + if (convert == 0) + return write(fileno(file), buf, count); + + p = buf; + ct = count; + while (ct--) { /* loop over the buffer */ + c = *p++; /* pick up a character */ + if (prevchar == '\r') { /* if prev char was cr */ + if (c == '\n') /* if have cr,lf then just */ + fseek(file, -1, 1); /* smash lf on top of the cr */ + else + if (c == '\0') /* if have cr,nul then */ + goto skipit; /* just skip over the putc */ + /* else just fall through and allow it */ + } + putc(c, file); +skipit: + prevchar = c; + } + return count; +} + + +/* When an error has occurred, it is possible that the two sides + * are out of synch. Ie: that what I think is the other side's + * response to packet N is really their response to packet N-1. + * + * So, to try to prevent that, we flush all the input queued up + * for us on the network connection on our host. + * + * We return the number of packets we flushed (mostly for reporting + * when trace is active). + */ + +int synchnet(int f) +{ + int j = 0; + char dummy; + + while (1) { + if (recv(f, &dummy, 1, MSG_DONTWAIT) < 0) + break; + j++; + } + return j; +} diff --git a/tracepath.c b/tracepath.c new file mode 100644 index 0000000..53bda16 --- /dev/null +++ b/tracepath.c @@ -0,0 +1,606 @@ +/* + * tracepath.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. + * + * Authors: Alexey Kuznetsov, + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef USE_IDN +#include +#define getnameinfo_flags NI_IDN +#else +#define getnameinfo_flags 0 +#endif + +#ifndef SOL_IPV6 +#define SOL_IPV6 IPPROTO_IPV6 +#endif + +#ifndef IP_PMTUDISC_DO +#define IP_PMTUDISC_DO 3 +#endif +#ifndef IPV6_PMTUDISC_DO +#define IPV6_PMTUDISC_DO 3 +#endif + +#define MAX_HOPS_LIMIT 255 +#define MAX_HOPS_DEFAULT 30 + +struct hhistory +{ + int hops; + struct timeval sendtime; +}; + +struct hhistory his[64]; +int hisptr; + +struct sockaddr_storage target; +socklen_t targetlen; +__u16 base_port; +int max_hops = MAX_HOPS_DEFAULT; + +int overhead; +int mtu; +void *pktbuf; +int hops_to = -1; +int hops_from = -1; +int no_resolve = 0; +int show_both = 0; +int mapped; + +#define HOST_COLUMN_SIZE 52 + +struct probehdr +{ + __u32 ttl; + struct timeval tv; +}; + +void data_wait(int fd) +{ + fd_set fds; + struct timeval tv; + FD_ZERO(&fds); + FD_SET(fd, &fds); + tv.tv_sec = 1; + tv.tv_usec = 0; + select(fd+1, &fds, NULL, NULL, &tv); +} + +void print_host(const char *a, const char *b, int both) +{ + int plen; + plen = printf("%s", a); + if (both) + plen += printf(" (%s)", b); + if (plen >= HOST_COLUMN_SIZE) + plen = HOST_COLUMN_SIZE - 1; + printf("%*s", HOST_COLUMN_SIZE - plen, ""); +} + +int recverr(int fd, struct addrinfo *ai, int ttl) +{ + int res; + struct probehdr rcvbuf; + char cbuf[512]; + struct iovec iov; + struct msghdr msg; + struct cmsghdr *cmsg; + struct sock_extended_err *e; + struct sockaddr_storage addr; + struct timeval tv; + struct timeval *rettv; + int slot = 0; + int rethops; + int sndhops; + int progress = -1; + int broken_router; + char hnamebuf[NI_MAXHOST] = ""; + +restart: + memset(&rcvbuf, -1, sizeof(rcvbuf)); + iov.iov_base = &rcvbuf; + iov.iov_len = sizeof(rcvbuf); + msg.msg_name = (__u8*)&addr; + msg.msg_namelen = sizeof(addr); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_flags = 0; + msg.msg_control = cbuf; + msg.msg_controllen = sizeof(cbuf); + + gettimeofday(&tv, NULL); + res = recvmsg(fd, &msg, MSG_ERRQUEUE); + if (res < 0) { + if (errno == EAGAIN) + return progress; + goto restart; + } + + progress = mtu; + + rethops = -1; + sndhops = -1; + e = NULL; + rettv = NULL; + + slot = -base_port; + switch (ai->ai_family) { + case AF_INET6: + slot += ntohs(((struct sockaddr_in6 *)&addr)->sin6_port); + break; + case AF_INET: + slot += ntohs(((struct sockaddr_in *)&addr)->sin_port); + break; + } + + if (slot >= 0 && slot < 63 && his[slot].hops) { + sndhops = his[slot].hops; + rettv = &his[slot].sendtime; + his[slot].hops = 0; + } + broken_router = 0; + if (res == sizeof(rcvbuf)) { + if (rcvbuf.ttl == 0 || rcvbuf.tv.tv_sec == 0) + broken_router = 1; + else { + sndhops = rcvbuf.ttl; + rettv = &rcvbuf.tv; + } + } + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + switch (cmsg->cmsg_level) { + case SOL_IPV6: + switch(cmsg->cmsg_type) { + case IPV6_RECVERR: + e = (struct sock_extended_err *)CMSG_DATA(cmsg); + break; + case IPV6_HOPLIMIT: +#ifdef IPV6_2292HOPLIMIT + case IPV6_2292HOPLIMIT: +#endif + memcpy(&rethops, CMSG_DATA(cmsg), sizeof(rethops)); + break; + default: + printf("cmsg6:%d\n ", cmsg->cmsg_type); + } + break; + case SOL_IP: + switch(cmsg->cmsg_type) { + case IP_RECVERR: + e = (struct sock_extended_err *)CMSG_DATA(cmsg); + break; + case IP_TTL: + rethops = *(__u8*)CMSG_DATA(cmsg); + break; + default: + printf("cmsg4:%d\n ", cmsg->cmsg_type); + } + } + } + if (e == NULL) { + printf("no info\n"); + return 0; + } + if (e->ee_origin == SO_EE_ORIGIN_LOCAL) + printf("%2d?: %-32s ", ttl, "[LOCALHOST]"); + else if (e->ee_origin == SO_EE_ORIGIN_ICMP6 || + e->ee_origin == SO_EE_ORIGIN_ICMP) { + char abuf[NI_MAXHOST]; + struct sockaddr *sa = (struct sockaddr *)(e + 1); + socklen_t salen; + + if (sndhops>0) + printf("%2d: ", sndhops); + else + printf("%2d?: ", ttl); + + switch (sa->sa_family) { + case AF_INET6: + salen = sizeof(struct sockaddr_in6); + break; + case AF_INET: + salen = sizeof(struct sockaddr_in); + break; + default: + salen = 0; + } + + if (no_resolve || show_both) { + if (getnameinfo(sa, salen, + abuf, sizeof(abuf), NULL, 0, + NI_NUMERICHOST)) + strcpy(abuf, "???"); + } else + abuf[0] = 0; + + if (!no_resolve || show_both) { + fflush(stdout); + if (getnameinfo(sa, salen, hnamebuf, sizeof hnamebuf, NULL, 0, getnameinfo_flags)) + strcpy(hnamebuf, "???"); + } else + hnamebuf[0] = 0; + + if (no_resolve) + print_host(abuf, hnamebuf, show_both); + else + print_host(hnamebuf, abuf, show_both); + } + + if (rettv) { + int diff = (tv.tv_sec-rettv->tv_sec)*1000000+(tv.tv_usec-rettv->tv_usec); + printf("%3d.%03dms ", diff/1000, diff%1000); + if (broken_router) + printf("(This broken router returned corrupted payload) "); + } + + if (rethops<=64) + rethops = 65-rethops; + else if (rethops<=128) + rethops = 129-rethops; + else + rethops = 256-rethops; + + switch (e->ee_errno) { + case ETIMEDOUT: + printf("\n"); + break; + case EMSGSIZE: + printf("pmtu %d\n", e->ee_info); + mtu = e->ee_info; + progress = mtu; + break; + case ECONNREFUSED: + printf("reached\n"); + hops_to = sndhops<0 ? ttl : sndhops; + hops_from = rethops; + return 0; + case EPROTO: + printf("!P\n"); + return 0; + case EHOSTUNREACH: + if ((e->ee_origin == SO_EE_ORIGIN_ICMP && + e->ee_type == 11 && + e->ee_code == 0) || + (e->ee_origin == SO_EE_ORIGIN_ICMP6 && + e->ee_type == 3 && + e->ee_code == 0)) { + if (rethops>=0) { + if (sndhops>=0 && rethops != sndhops) + printf("asymm %2d ", rethops); + else if (sndhops<0 && rethops != ttl) + printf("asymm %2d ", rethops); + } + printf("\n"); + break; + } + printf("!H\n"); + return 0; + case ENETUNREACH: + printf("!N\n"); + return 0; + case EACCES: + printf("!A\n"); + return 0; + default: + printf("\n"); + errno = e->ee_errno; + perror("NET ERROR"); + return 0; + } + goto restart; +} + +int probe_ttl(int fd, struct addrinfo *ai, int ttl) +{ + int i; + struct probehdr *hdr = pktbuf; + + memset(pktbuf, 0, mtu); +restart: + for (i=0; i<10; i++) { + int res; + + hdr->ttl = ttl; + switch (ai->ai_family) { + case AF_INET6: + ((struct sockaddr_in6 *)&target)->sin6_port = htons(base_port + hisptr); + break; + case AF_INET: + ((struct sockaddr_in *)&target)->sin_port = htons(base_port + hisptr); + break; + } + gettimeofday(&hdr->tv, NULL); + his[hisptr].hops = ttl; + his[hisptr].sendtime = hdr->tv; + if (sendto(fd, pktbuf, mtu-overhead, 0, (struct sockaddr *)&target, targetlen) > 0) + break; + res = recverr(fd, ai, ttl); + his[hisptr].hops = 0; + if (res==0) + return 0; + if (res > 0) + goto restart; + } + hisptr = (hisptr + 1) & 63; + + if (i<10) { + data_wait(fd); + if (recv(fd, pktbuf, mtu, MSG_DONTWAIT) > 0) { + printf("%2d?: reply received 8)\n", ttl); + return 0; + } + return recverr(fd, ai, ttl); + } + + printf("%2d: send failed\n", ttl); + return 0; +} + +static void usage(void) __attribute((noreturn)); + +static void usage(void) +{ + fprintf(stderr, "Usage: tracepath [-4] [-6] [-n] [-b] [-l ] [-p port] \n"); + exit(-1); +} + + +int main(int argc, char **argv) +{ + struct addrinfo hints = { + .ai_family = AF_UNSPEC, + .ai_socktype = SOCK_DGRAM, + .ai_protocol = IPPROTO_UDP, +#ifdef USE_IDN + .ai_flags = AI_IDN | AI_CANONNAME, +#endif + }; + struct addrinfo *ai, *result; + int ch; + int status; + int fd; + int on; + int ttl; + char *p; + char pbuf[NI_MAXSERV]; + +#ifdef USE_IDN + setlocale(LC_ALL, ""); +#endif + + /* Support being called using `tracepath4` or `tracepath6` symlinks */ + if (argv[0][strlen(argv[0])-1] == '4') + hints.ai_family = AF_INET; + else if (argv[0][strlen(argv[0])-1] == '6') + hints.ai_family = AF_INET6; + + while ((ch = getopt(argc, argv, "46nbh?l:m:p:")) != EOF) { + switch(ch) { + case '4': + if (hints.ai_family != AF_UNSPEC) { + fprintf(stderr, "tracepath: Only one -4 or -6 option may be specified\n"); + exit(2); + } + hints.ai_family = AF_INET; + break; + case '6': + if (hints.ai_family != AF_UNSPEC) { + fprintf(stderr, "tracepath: Only one -4 or -6 option may be specified\n"); + exit(2); + } + hints.ai_family = AF_INET6; + break; + case 'n': + no_resolve = 1; + break; + case 'b': + show_both = 1; + break; + case 'l': + if ((mtu = atoi(optarg)) <= overhead) { + fprintf(stderr, "Error: pktlen must be > %d and <= %d.\n", + overhead, INT_MAX); + exit(1); + } + break; + case 'm': + max_hops = atoi(optarg); + if (max_hops < 0 || max_hops > MAX_HOPS_LIMIT) { + fprintf(stderr, + "Error: max hops must be 0 .. %d (inclusive).\n", + MAX_HOPS_LIMIT); + } + break; + case 'p': + base_port = atoi(optarg); + break; + default: + usage(); + } + } + + argc -= optind; + argv += optind; + + if (argc != 1) + usage(); + + /* Backward compatiblity */ + if (!base_port) { + p = strchr(argv[0], '/'); + if (p) { + *p = 0; + base_port = atoi(p+1); + } else + base_port = 44444; + } + sprintf(pbuf, "%u", base_port); + + status = getaddrinfo(argv[0], pbuf, &hints, &result); + if (status) { + fprintf(stderr, "tracepath: %s: %s\n", argv[0], gai_strerror(status)); + exit(1); + } + + fd = -1; + for (ai = result; ai; ai = ai->ai_next) { + if (ai->ai_family != AF_INET6 && + ai->ai_family != AF_INET) + continue; + fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (fd < 0) + continue; + memcpy(&target, ai->ai_addr, sizeof(*ai->ai_addr)); + targetlen = ai->ai_addrlen; + break; + } + if (fd < 0) { + perror("socket/connect"); + exit(1); + } + + switch (ai->ai_family) { + case AF_INET6: + overhead = 48; + if (!mtu) + mtu = 128000; + if (mtu <= overhead) + goto pktlen_error; + + on = IPV6_PMTUDISC_DO; + if (setsockopt(fd, SOL_IPV6, IPV6_MTU_DISCOVER, &on, sizeof(on)) && + (on = IPV6_PMTUDISC_DO, + setsockopt(fd, SOL_IPV6, IPV6_MTU_DISCOVER, &on, sizeof(on)))) { + perror("IPV6_MTU_DISCOVER"); + exit(1); + } + on = 1; + if (setsockopt(fd, SOL_IPV6, IPV6_RECVERR, &on, sizeof(on))) { + perror("IPV6_RECVERR"); + exit(1); + } + if ( +#ifdef IPV6_RECVHOPLIMIT + setsockopt(fd, SOL_IPV6, IPV6_HOPLIMIT, &on, sizeof(on)) && + setsockopt(fd, SOL_IPV6, IPV6_2292HOPLIMIT, &on, sizeof(on)) +#else + setsockopt(fd, SOL_IPV6, IPV6_HOPLIMIT, &on, sizeof(on)) +#endif + ) { + perror("IPV6_HOPLIMIT"); + exit(1); + } + if (!IN6_IS_ADDR_V4MAPPED(&(((struct sockaddr_in6 *)&target)->sin6_addr))) + break; + mapped = 1; + /*FALLTHROUGH*/ + case AF_INET: + overhead = 28; + if (!mtu) + mtu = 65535; + if (mtu <= overhead) + goto pktlen_error; + + on = IP_PMTUDISC_DO; + if (setsockopt(fd, SOL_IP, IP_MTU_DISCOVER, &on, sizeof(on))) { + perror("IP_MTU_DISCOVER"); + exit(1); + } + on = 1; + if (setsockopt(fd, SOL_IP, IP_RECVERR, &on, sizeof(on))) { + perror("IP_RECVERR"); + exit(1); + } + if (setsockopt(fd, SOL_IP, IP_RECVTTL, &on, sizeof(on))) { + perror("IP_RECVTTL"); + exit(1); + } + } + + pktbuf = malloc(mtu); + if (!pktbuf) { + perror("malloc"); + exit(1); + } + + for (ttl = 1; ttl <= max_hops; ttl++) { + int res; + int i; + + on = ttl; + switch (ai->ai_family) { + case AF_INET6: + if (setsockopt(fd, SOL_IPV6, IPV6_UNICAST_HOPS, &on, sizeof(on))) { + perror("IPV6_UNICAST_HOPS"); + exit(1); + } + if (!mapped) + break; + /*FALLTHROUGH*/ + case AF_INET: + if (setsockopt(fd, SOL_IP, IP_TTL, &on, sizeof(on))) { + perror("IP_TTL"); + exit(1); + } + } + +restart: + for (i=0; i<3; i++) { + int old_mtu; + + old_mtu = mtu; + res = probe_ttl(fd, ai, ttl); + if (mtu != old_mtu) + goto restart; + if (res == 0) + goto done; + if (res > 0) + break; + } + + if (res < 0) + printf("%2d: no reply\n", ttl); + } + printf(" Too many hops: pmtu %d\n", mtu); + +done: + freeaddrinfo(result); + + printf(" Resume: pmtu %d ", mtu); + if (hops_to>=0) + printf("hops %d ", hops_to); + if (hops_from>=0) + printf("back %d ", hops_from); + printf("\n"); + exit(0); + +pktlen_error: + fprintf(stderr, "Error: pktlen must be > %d and <= %d\n", + overhead, INT_MAX); + exit(1); +} diff --git a/traceroute6.c b/traceroute6.c new file mode 100644 index 0000000..a1022f9 --- /dev/null +++ b/traceroute6.c @@ -0,0 +1,942 @@ +/* + * Modified for NRL 4.4BSD IPv6 release. + * 07/31/96 bgp + * + * Search for "#ifdef NRL" to find the changes. + */ + +/* + * Modified for Linux IPv6 by Pedro Roque + * 31/07/1996 + * + * As ICMP error messages for IPv6 now include more than 8 bytes + * UDP datagrams are now sent via an UDP socket instead of magic + * RAW socket tricks. + * + * Original copyright and comments left intact. They might not + * match the code anymore. + */ + +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Van Jacobson. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * traceroute host - trace the route ip packets follow going to "host". + * + * Attempt to trace the route an ip packet would follow to some + * internet host. We find out intermediate hops 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" (which means we got to "host") or hit a max (which + * defaults to 30 hops & can be changed with the -m flag). Three + * probes (change with -q flag) 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. 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 sec. timeout + * interval (changed with the -w flag), a "*" is printed for that + * probe. + * + * Probe packets are UDP format. We don't want the destination + * host to process them so the destination port is set to an + * unlikely value (if some clod on the destination is using that + * value, it can be changed with the -p flag). + * + * A sample use might be: + * + * [yak 71]% traceroute nis.nsf.net. + * traceroute to nis.nsf.net (35.1.1.48), 30 hops max, 56 byte packet + * 1 helios.ee.lbl.gov (128.3.112.1) 19 ms 19 ms 0 ms + * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 39 ms 19 ms + * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 39 ms 19 ms + * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 39 ms 40 ms 39 ms + * 5 ccn-nerif22.Berkeley.EDU (128.32.168.22) 39 ms 39 ms 39 ms + * 6 128.32.197.4 (128.32.197.4) 40 ms 59 ms 59 ms + * 7 131.119.2.5 (131.119.2.5) 59 ms 59 ms 59 ms + * 8 129.140.70.13 (129.140.70.13) 99 ms 99 ms 80 ms + * 9 129.140.71.6 (129.140.71.6) 139 ms 239 ms 319 ms + * 10 129.140.81.7 (129.140.81.7) 220 ms 199 ms 199 ms + * 11 nic.merit.edu (35.1.1.48) 239 ms 239 ms 239 ms + * + * Note that lines 2 & 3 are the same. This is due to a buggy + * kernel on the 2nd hop system -- lbl-csam.arpa -- that forwards + * packets with a zero ttl. + * + * A more interesting example is: + * + * [yak 72]% traceroute allspice.lcs.mit.edu. + * traceroute to allspice.lcs.mit.edu (18.26.0.115), 30 hops max + * 1 helios.ee.lbl.gov (128.3.112.1) 0 ms 0 ms 0 ms + * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 19 ms 19 ms 19 ms + * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 19 ms 19 ms + * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 19 ms 39 ms 39 ms + * 5 ccn-nerif22.Berkeley.EDU (128.32.168.22) 20 ms 39 ms 39 ms + * 6 128.32.197.4 (128.32.197.4) 59 ms 119 ms 39 ms + * 7 131.119.2.5 (131.119.2.5) 59 ms 59 ms 39 ms + * 8 129.140.70.13 (129.140.70.13) 80 ms 79 ms 99 ms + * 9 129.140.71.6 (129.140.71.6) 139 ms 139 ms 159 ms + * 10 129.140.81.7 (129.140.81.7) 199 ms 180 ms 300 ms + * 11 129.140.72.17 (129.140.72.17) 300 ms 239 ms 239 ms + * 12 * * * + * 13 128.121.54.72 (128.121.54.72) 259 ms 499 ms 279 ms + * 14 * * * + * 15 * * * + * 16 * * * + * 17 * * * + * 18 ALLSPICE.LCS.MIT.EDU (18.26.0.115) 339 ms 279 ms 279 ms + * + * (I start to see why I'm having so much trouble with mail to + * MIT.) Note that the gateways 12, 14, 15, 16 & 17 hops away + * either don't send ICMP "time exceeded" messages or send them + * with a ttl too small to reach us. 14 - 17 are running the + * MIT C Gateway code that doesn't send "time exceeded"s. God + * only knows what's going on with 12. + * + * The silent gateway 12 in the above may be the result of a bug in + * the 4.[23]BSD network code (and its derivatives): 4.x (x <= 3) + * sends an unreachable message using whatever ttl remains in the + * original datagram. Since, for gateways, the remaining ttl is + * zero, the icmp "time exceeded" is guaranteed to not make it back + * to us. The behavior of this bug is slightly more interesting + * when it appears on the destination system: + * + * 1 helios.ee.lbl.gov (128.3.112.1) 0 ms 0 ms 0 ms + * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 19 ms 39 ms + * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 19 ms 39 ms 19 ms + * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 39 ms 40 ms 19 ms + * 5 ccn-nerif35.Berkeley.EDU (128.32.168.35) 39 ms 39 ms 39 ms + * 6 csgw.Berkeley.EDU (128.32.133.254) 39 ms 59 ms 39 ms + * 7 * * * + * 8 * * * + * 9 * * * + * 10 * * * + * 11 * * * + * 12 * * * + * 13 rip.Berkeley.EDU (128.32.131.22) 59 ms ! 39 ms ! 39 ms ! + * + * Notice that there are 12 "gateways" (13 is the final + * destination) and exactly the last half of them are "missing". + * What's really happening is that rip (a Sun-3 running Sun OS3.5) + * is using the ttl from our arriving datagram as the ttl in its + * icmp reply. So, the reply will time out on the return path + * (with no notice sent to anyone since icmp's aren't sent for + * icmp's) until we probe with a ttl that's at least twice the path + * length. I.e., rip is really only 7 hops away. A reply that + * returns with a ttl of 1 is a clue this problem exists. + * Traceroute prints a "!" after the time if the ttl is <= 1. + * Since vendors ship a lot of obsolete (DEC's Ultrix, Sun 3.x) or + * non-standard (HPUX) software, expect to see this problem + * frequently and/or take care picking the target host of your + * probes. + * + * Other possible annotations after the time are !H, !N, !P (got a host, + * network or protocol unreachable, respectively), !S or !F (source + * route failed or fragmentation needed -- neither of these should + * ever occur and the associated gateway is busted if you see one), + * !X (communication administratively prohibited). If + * almost all the probes result in some kind of unreachable, traceroute + * will give up and exit. + * + * Notes + * ----- + * This program must be run by root or be setuid. (I suggest that + * you *don't* make it setuid -- casual use could result in a lot + * of unnecessary traffic on our poor, congested nets.) + * + * This program requires a kernel mod that does not appear in any + * system available from Berkeley: A raw ip socket using proto + * IPPROTO_RAW must interpret the data sent as an ip datagram (as + * opposed to data to be wrapped in a ip datagram). See the README + * file that came with the source to this program for a description + * of the mods I made to /sys/netinet/raw_ip.c. Your mileage may + * vary. But, again, ANY 4.x (x < 4) BSD KERNEL WILL HAVE TO BE + * MODIFIED TO RUN THIS PROGRAM. + * + * The udp port usage may appear bizarre (well, ok, it is bizarre). + * The problem is that an icmp message only contains 8 bytes of + * data from the original datagram. 8 bytes is the size of a udp + * header so, if we want to associate replies with the original + * datagram, the necessary information must be encoded into the + * udp header (the ip id could be used but there's no way to + * interlock with the kernel's assignment of ip id's and, anyway, + * it would have taken a lot more kernel hacking to allow this + * code to set the ip id). So, to allow two or more users to + * use traceroute simultaneously, we use this task's pid as the + * source port (the high bit is set to move the port number out + * of the "likely" range). To keep track of which probe is being + * replied to (so times and/or hop counts don't get confused by a + * reply that was delayed in transit), we increment the destination + * port number before each probe. + * + * Don't use this as a coding example. I was trying to find a + * routing problem and this code sort-of popped out after 48 hours + * without sleep. I was amazed it ever compiled, much less ran. + * + * I stole the idea for this program from Steve Deering. Since + * the first release, I've learned that had I attended the right + * IETF working group meetings, I also could have stolen it from Guy + * Almes or Matt Mathis. I don't know (or care) who came up with + * the idea first. I envy the originators' perspicacity and I'm + * glad they didn't keep the idea a secret. + * + * Tim Seaver, Ken Adelman and C. Philip Wood provided bug fixes and/or + * enhancements to the original distribution. + * + * I've hacked up a round-trip-route version of this that works by + * sending a loose-source-routed udp datagram through the destination + * back to yourself. Unfortunately, SO many gateways botch source + * routing, the thing is almost worthless. Maybe one day... + * + * -- Van Jacobson (van@helios.ee.lbl.gov) + * Tue Dec 20 03:50:13 PST 1988 + */ + +#include +#include +#include +#include +#include +#include + +#if __linux__ +#include +#endif +#include +#include +#include +#include +#include + +#include +#include +#include +#ifdef CAPABILITIES +#include +#endif + +#ifdef USE_IDN +#include + +#define ADDRINFO_IDN_FLAGS AI_IDN +#define getnameinfo_flags NI_IDN +#else +#define getnameinfo_flags 0 +#define ADDRINFO_IDN_FLAGS 0 +#endif + +#include + +#include +#include +#include +#include +#include +#include + +#include "SNAPSHOT.h" + +#ifndef SOL_IPV6 +#define SOL_IPV6 IPPROTO_IPV6 +#endif + +#define MAXPACKET 65535 + +#ifndef FD_SET +#define NFDBITS (8*sizeof(fd_set)) +#define FD_SETSIZE NFDBITS +#define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS))) +#define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS))) +#define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS))) +#define FD_ZERO(p) memset((char *)(p), 0, sizeof(*(p))) +#endif + +#define Fprintf (void)fprintf +#define Printf (void)printf + +unsigned char packet[512]; /* last inbound (icmp) packet */ + +int wait_for_reply(int, struct sockaddr_in6 *, struct in6_addr *, int); +int packet_ok(unsigned char *buf, int cc, struct sockaddr_in6 *from, + struct in6_addr *to, int seq, struct timeval *); +void send_probe(int seq, int ttl); +double deltaT (struct timeval *, struct timeval *); +void print(unsigned char *buf, int cc, struct sockaddr_in6 *from); +void tvsub (struct timeval *, struct timeval *); +void usage(void); + +int icmp_sock; /* receive (icmp) socket file descriptor */ +int sndsock; /* send (udp) socket file descriptor */ +struct timezone tz; /* leftover */ + +struct sockaddr_in6 whereto; /* Who to try to reach */ + +struct sockaddr_in6 saddr; +struct sockaddr_in6 firsthop; +char *source = NULL; +char *device = NULL; +char *hostname; + +int nprobes = 3; +int max_ttl = 30; +pid_t ident; +unsigned short port = 32768+666; /* start udp dest port # for probe packets */ +int options; /* socket options */ +int verbose; +int waittime = 5; /* time to wait for response (in seconds) */ +int nflag; /* print addresses numerically */ + + +struct pkt_format +{ + __u32 ident; + __u32 seq; + struct timeval tv; +}; + +char *sendbuff; +int datalen = sizeof(struct pkt_format); + + + +int main(int argc, char *argv[]) +{ + char pa[NI_MAXHOST]; + extern char *optarg; + extern int optind; + struct addrinfo hints6 = { .ai_family = AF_INET6, .ai_socktype = SOCK_RAW, + .ai_flags = AI_CANONNAME|ADDRINFO_IDN_FLAGS }; + struct addrinfo *result; + int status; + struct sockaddr_in6 from, *to; + int ch, i, on, probe, seq, tos, ttl; + int socket_errno; + char *resolved_hostname = NULL; + + icmp_sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); + socket_errno = errno; + + if (setuid(getuid())) { + perror("traceroute6: setuid"); + exit(-1); + } +#ifdef CAPABILITIES + { + cap_t caps = cap_init(); + if (cap_set_proc(caps)) { + perror("traceroute6: cap_set_proc"); + exit(-1); + } + cap_free(caps); + } +#endif + +#ifdef USE_IDN + setlocale(LC_ALL, ""); +#endif + + on = 1; + seq = tos = 0; + to = (struct sockaddr_in6 *)&whereto; + while ((ch = getopt(argc, argv, "dm:np:q:rs:t:w:vi:g:V")) != EOF) { + switch(ch) { + case 'd': + options |= SO_DEBUG; + break; + case 'm': + max_ttl = atoi(optarg); + if (max_ttl <= 1) { + Fprintf(stderr, + "traceroute: max ttl must be >1.\n"); + exit(1); + } + break; + case 'n': + nflag++; + break; + case 'p': + port = atoi(optarg); + if (port < 1) { + Fprintf(stderr, + "traceroute: port must be >0.\n"); + exit(1); + } + break; + case 'q': + nprobes = atoi(optarg); + if (nprobes < 1) { + Fprintf(stderr, + "traceroute: nprobes must be >0.\n"); + exit(1); + } + break; + case 'r': + options |= SO_DONTROUTE; + break; + case 's': + /* + * set the ip source address of the outbound + * probe (e.g., on a multi-homed host). + */ + source = optarg; + break; + case 'i': + device = optarg; + break; + case 'g': + Fprintf(stderr, "Sorry, rthdr is not yet supported\n"); + break; + case 'v': + verbose++; + break; + case 'w': + waittime = atoi(optarg); + if (waittime <= 1) { + Fprintf(stderr, + "traceroute: wait must be >1 sec.\n"); + exit(1); + } + break; + case 'V': + printf("traceroute6 utility, iputils-%s\n", SNAPSHOT); + exit(0); + default: + usage(); + } + } + argc -= optind; + argv += optind; + + if (argc < 1) + usage(); + + setlinebuf (stdout); + + (void) memset((char *)&whereto, 0, sizeof(whereto)); + + to->sin6_family = AF_INET6; + + if (inet_pton(AF_INET6, *argv, &to->sin6_addr) > 0) { + hostname = *argv; + } else { + status = getaddrinfo(*argv, NULL, &hints6, &result); + if (status) { + (void)fprintf(stderr, + "traceroute: %s: %s\n", *argv, gai_strerror(status)); + exit(1); + } + + memcpy(to, result->ai_addr, sizeof *to); + resolved_hostname = strdup(result->ai_canonname); + if (resolved_hostname == NULL) { + (void)fprintf(stderr, + "traceroute: cannot allocate memory\n"); + exit(1); + } + hostname = resolved_hostname; + freeaddrinfo(result); + } + + to->sin6_port = htons(port); + + firsthop = *to; + if (*++argv) { + datalen = atoi(*argv); + /* Message for rpm maintainers: have _shame_. If you want + * to fix something send the patch to me for sanity checking. + * "datalen" patch is a shit. */ + if (datalen == 0) + datalen = sizeof(struct pkt_format); + else if (datalen < (int)sizeof(struct pkt_format) || + datalen >= MAXPACKET) { + Fprintf(stderr, + "traceroute: packet size must be %d <= s < %d.\n", + (int)sizeof(struct pkt_format), MAXPACKET); + exit(1); + } + } + + ident = getpid(); + + sendbuff = malloc(datalen); + if (sendbuff == NULL) { + fprintf(stderr, "malloc failed\n"); + exit(1); + } + + if (icmp_sock < 0) { + errno = socket_errno; + perror("traceroute6: icmp socket"); + exit(1); + } + +#ifdef IPV6_RECVPKTINFO + setsockopt(icmp_sock, SOL_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)); + setsockopt(icmp_sock, SOL_IPV6, IPV6_2292PKTINFO, &on, sizeof(on)); +#else + setsockopt(icmp_sock, SOL_IPV6, IPV6_PKTINFO, &on, sizeof(on)); +#endif + + if (options & SO_DEBUG) + setsockopt(icmp_sock, SOL_SOCKET, SO_DEBUG, + (char *)&on, sizeof(on)); + if (options & SO_DONTROUTE) + setsockopt(icmp_sock, SOL_SOCKET, SO_DONTROUTE, + (char *)&on, sizeof(on)); + +#ifdef __linux__ + on = 2; + if (setsockopt(icmp_sock, SOL_RAW, IPV6_CHECKSUM, &on, sizeof(on)) < 0) { + /* checksum should be enabled by default and setting this + * option might fail anyway. + */ + fprintf(stderr, "setsockopt(RAW_CHECKSUM) failed - try to continue."); + } +#endif + + if ((sndsock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { + perror("traceroute: UDP socket"); + exit(5); + } +#ifdef SO_SNDBUF + if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, (char *)&datalen, + sizeof(datalen)) < 0) { + perror("traceroute: SO_SNDBUF"); + exit(6); + } +#endif /* SO_SNDBUF */ + + if (options & SO_DEBUG) + (void) setsockopt(sndsock, SOL_SOCKET, SO_DEBUG, + (char *)&on, sizeof(on)); + if (options & SO_DONTROUTE) + (void) setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE, + (char *)&on, sizeof(on)); + + if (source == NULL) { + socklen_t alen; + int probe_fd = socket(AF_INET6, SOCK_DGRAM, 0); + + if (probe_fd < 0) { + perror("socket"); + exit(1); + } + if (device) { + if (setsockopt(probe_fd, SOL_SOCKET, SO_BINDTODEVICE, device, strlen(device)+1) == -1) + perror("WARNING: interface is ignored"); + } + firsthop.sin6_port = htons(1025); + if (connect(probe_fd, (struct sockaddr*)&firsthop, sizeof(firsthop)) == -1) { + perror("connect"); + exit(1); + } + alen = sizeof(saddr); + if (getsockname(probe_fd, (struct sockaddr*)&saddr, &alen) == -1) { + perror("getsockname"); + exit(1); + } + saddr.sin6_port = 0; + close(probe_fd); + } else { + (void) memset((char *)&saddr, 0, sizeof(saddr)); + saddr.sin6_family = AF_INET6; + if (inet_pton(AF_INET6, source, &saddr.sin6_addr) <= 0) + { + Printf("traceroute: unknown addr %s\n", source); + exit(1); + } + } + + if (bind(sndsock, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) { + perror ("traceroute: bind sending socket"); + exit (1); + } + if (bind(icmp_sock, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) { + perror ("traceroute: bind icmp6 socket"); + exit (1); + } + + Fprintf(stderr, "traceroute to %s (%s)", hostname, + inet_ntop(AF_INET6, &to->sin6_addr, pa, sizeof(pa))); + + Fprintf(stderr, " from %s", + inet_ntop(AF_INET6, &saddr.sin6_addr, pa, sizeof(pa))); + Fprintf(stderr, ", %d hops max, %d byte packets\n", max_ttl, datalen); + (void) fflush(stderr); + + for (ttl = 1; ttl <= max_ttl; ++ttl) { + struct in6_addr lastaddr = {{{0,}}}; + int got_there = 0; + int unreachable = 0; + + Printf("%2d ", ttl); + for (probe = 0; probe < nprobes; ++probe) { + int cc, reset_timer; + struct timeval t1, t2; + struct timezone tz; + struct in6_addr to; + + gettimeofday(&t1, &tz); + send_probe(++seq, ttl); + reset_timer = 1; + + while ((cc = wait_for_reply(icmp_sock, &from, &to, reset_timer)) != 0) { + gettimeofday(&t2, &tz); + if ((i = packet_ok(packet, cc, &from, &to, seq, &t1))) { + if (memcmp(&from.sin6_addr, &lastaddr, sizeof(from.sin6_addr))) { + print(packet, cc, &from); + memcpy(&lastaddr, + &from.sin6_addr, + sizeof(lastaddr)); + } + Printf(" %g ms", deltaT(&t1, &t2)); + switch(i - 1) { + case ICMP6_DST_UNREACH_NOPORT: + ++got_there; + break; + + case ICMP6_DST_UNREACH_NOROUTE: + ++unreachable; + Printf(" !N"); + break; + case ICMP6_DST_UNREACH_ADDR: + ++unreachable; + Printf(" !H"); + break; + + case ICMP6_DST_UNREACH_ADMIN: + ++unreachable; + Printf(" !X"); + break; + } + break; + } else + reset_timer = 0; + } + if (cc <= 0) + Printf(" *"); + (void) fflush(stdout); + } + putchar('\n'); + if (got_there || + (unreachable > 0 && unreachable >= nprobes-1)) + break; + } + + if (resolved_hostname != NULL) { + free(resolved_hostname); + } + + return 0; +} + +int +wait_for_reply(sock, from, to, reset_timer) + int sock; + struct sockaddr_in6 *from; + struct in6_addr *to; + int reset_timer; +{ + fd_set fds; + static struct timeval wait; + int cc = 0; + char cbuf[512]; + + FD_ZERO(&fds); + FD_SET(sock, &fds); + if (reset_timer) { + /* + * traceroute could hang if someone else has a ping + * running and our ICMP reply gets dropped but we don't + * realize it because we keep waking up to handle those + * other ICMP packets that keep coming in. To fix this, + * "reset_timer" will only be true if the last packet that + * came in was for us or if this is the first time we're + * waiting for a reply since sending out a probe. Note + * that this takes advantage of the select() feature on + * Linux where the remaining timeout is written to the + * struct timeval area. + */ + wait.tv_sec = waittime; + wait.tv_usec = 0; + } + + if (select(sock+1, &fds, (fd_set *)0, (fd_set *)0, &wait) > 0) { + struct iovec iov; + struct msghdr msg; + iov.iov_base = packet; + iov.iov_len = sizeof(packet); + msg.msg_name = (void *)from; + msg.msg_namelen = sizeof(*from); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_flags = 0; + msg.msg_control = cbuf; + msg.msg_controllen = sizeof(cbuf); + + cc = recvmsg(icmp_sock, &msg, 0); + if (cc >= 0) { + struct cmsghdr *cmsg; + struct in6_pktinfo *ipi; + + for (cmsg = CMSG_FIRSTHDR(&msg); + cmsg; + cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level != SOL_IPV6) + continue; + switch (cmsg->cmsg_type) { + case IPV6_PKTINFO: +#ifdef IPV6_2292PKTINFO + case IPV6_2292PKTINFO: +#endif + ipi = (struct in6_pktinfo *)CMSG_DATA(cmsg); + memcpy(to, ipi, sizeof(*to)); + } + } + } + } + + return(cc); +} + + +void send_probe(int seq, int ttl) +{ + struct pkt_format *pkt = (struct pkt_format *) sendbuff; + int i; + + pkt->ident = htonl(ident); + pkt->seq = htonl(seq); + gettimeofday(&pkt->tv, &tz); + + i = setsockopt(sndsock, SOL_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)); + if (i < 0) + { + perror("setsockopt"); + exit(1); + } + + do { + i = sendto(sndsock, sendbuff, datalen, 0, + (struct sockaddr *)&whereto, sizeof(whereto)); + } while (i<0 && errno == ECONNREFUSED); + + if (i < 0 || i != datalen) { + if (i<0) + perror("sendto"); + Printf("traceroute: wrote %s %d chars, ret=%d\n", hostname, + datalen, i); + (void) fflush(stdout); + } +} + + +double deltaT(struct timeval *t1p, struct timeval *t2p) +{ + register double dt; + + dt = (double)(t2p->tv_sec - t1p->tv_sec) * 1000.0 + + (double)(t2p->tv_usec - t1p->tv_usec) / 1000.0; + return (dt); +} + + +/* + * Convert an ICMP "type" field to a printable string. + */ +char * pr_type(unsigned char t) +{ + switch(t) { + /* Unknown */ + case 0: + return "Error"; + case 1: + /* ICMP6_DST_UNREACH: */ + return "Destination Unreachable"; + case 2: + /* ICMP6_PACKET_TOO_BIG: */ + return "Packet Too Big"; + case 3: + /* ICMP6_TIME_EXCEEDED */ + return "Time Exceeded in Transit"; + case 4: + /* ICMP6_PARAM_PROB */ + return "Parameter Problem"; + case 128: + /* ICMP6_ECHO_REQUEST */ + return "Echo Request"; + case 129: + /* ICMP6_ECHO_REPLY */ + return "Echo Reply"; + case 130: + /* ICMP6_MEMBERSHIP_QUERY */ + return "Membership Query"; + case 131: + /* ICMP6_MEMBERSHIP_REPORT */ + return "Membership Report"; + case 132: + /* ICMP6_MEMBERSHIP_REDUCTION */ + return "Membership Reduction"; + case 133: + /* ND_ROUTER_SOLICIT */ + return "Router Solicitation"; + case 134: + /* ND_ROUTER_ADVERT */ + return "Router Advertisement"; + case 135: + /* ND_NEIGHBOR_SOLICIT */ + return "Neighbor Solicitation"; + case 136: + /* ND_NEIGHBOR_ADVERT */ + return "Neighbor Advertisement"; + case 137: + /* ND_REDIRECT */ + return "Redirect"; + } + + return("OUT-OF-RANGE"); +} + + +int packet_ok(unsigned char *buf, int cc, struct sockaddr_in6 *from, + struct in6_addr *to, int seq, + struct timeval *tv) +{ + struct icmp6_hdr *icp; + unsigned char type, code; + + icp = (struct icmp6_hdr *) buf; + + type = icp->icmp6_type; + code = icp->icmp6_code; + + if ((type == ICMP6_TIME_EXCEEDED && code == ICMP6_TIME_EXCEED_TRANSIT) || + type == ICMP6_DST_UNREACH) + { + struct ip6_hdr *hip; + struct udphdr *up; + int nexthdr; + + hip = (struct ip6_hdr *) (icp + 1); + up = (struct udphdr *)(hip+1); + nexthdr = hip->ip6_nxt; + + if (nexthdr == 44) { + nexthdr = *(unsigned char*)up; + up++; + } + if (nexthdr == IPPROTO_UDP) + { + struct pkt_format *pkt; + + pkt = (struct pkt_format *) (up + 1); + + if (ntohl(pkt->ident) == ident && + ntohl(pkt->seq) == seq) + { + *tv = pkt->tv; + return (type == ICMP6_TIME_EXCEEDED ? -1 : code+1); + } + } + + } + + if (verbose) { + unsigned char *p; + char pa1[NI_MAXHOST]; + char pa2[NI_MAXHOST]; + int i; + + p = (unsigned char *) (icp + 1); + + Printf("\n%d bytes from %s to %s", cc, + inet_ntop(AF_INET6, &from->sin6_addr, pa1, sizeof(pa1)), + inet_ntop(AF_INET6, to, pa2, sizeof(pa2))); + + Printf(": icmp type %d (%s) code %d\n", type, pr_type(type), + icp->icmp6_code); + + cc -= sizeof(struct icmp6_hdr); + for (i = 0; i < cc ; i++) { + if (i % 16 == 0) + Printf("%04x:", i); + if (i % 4 == 0) + Printf(" "); + Printf("%02x", 0xff & (unsigned)p[i]); + if (i % 16 == 15 && i + 1 < cc) + Printf("\n"); + } + Printf("\n"); + } + + return(0); +} + + +void print(unsigned char *buf, int cc, struct sockaddr_in6 *from) +{ + char pa[NI_MAXHOST] = ""; + char hnamebuf[NI_MAXHOST] = ""; + + if (nflag) + Printf(" %s", inet_ntop(AF_INET6, &from->sin6_addr, + pa, sizeof(pa))); + else { + inet_ntop(AF_INET6, &from->sin6_addr, pa, sizeof(pa)); + getnameinfo((struct sockaddr *) from, sizeof *from, hnamebuf, sizeof hnamebuf, NULL, 0, getnameinfo_flags); + + Printf(" %s (%s)", hnamebuf[0] ? hnamebuf : pa, pa); + } +} + + +/* + * Subtract 2 timeval structs: out = out - in. + * Out is assumed to be >= in. + */ +void +tvsub(out, in) + register struct timeval *out, *in; +{ + if ((out->tv_usec -= in->tv_usec) < 0) { + out->tv_sec--; + out->tv_usec += 1000000; + } + out->tv_sec -= in->tv_sec; +} + +void usage(void) +{ + fprintf(stderr, +"Usage: traceroute6 [-dnrvV] [-m max_ttl] [-p port#] [-q nqueries]\n\t\ +[-s src_addr] [-t tos] [-w wait] host [data size]\n"); + exit(1); +}