diff options
author | Anas Nashif <anas.nashif@intel.com> | 2012-12-05 02:05:02 -0800 |
---|---|---|
committer | Anas Nashif <anas.nashif@intel.com> | 2012-12-05 02:05:02 -0800 |
commit | c098f623bf888728e262e7813ffcdfe02df74937 (patch) | |
tree | 6188990bce1cee38fc0fd44ff2c294b877118887 | |
download | iputils-c098f623bf888728e262e7813ffcdfe02df74937.tar.gz iputils-c098f623bf888728e262e7813ffcdfe02df74937.tar.bz2 iputils-c098f623bf888728e262e7813ffcdfe02df74937.zip |
Imported Upstream version 20121126upstream/s20121126upstream/20121126upstream
-rw-r--r-- | INSTALL | 24 | ||||
-rw-r--r-- | Makefile | 211 | ||||
-rw-r--r-- | Modules/Makefile | 12 | ||||
-rw-r--r-- | Modules/pg3.c | 735 | ||||
-rw-r--r-- | RELNOTES | 598 | ||||
-rw-r--r-- | SNAPSHOT.h | 1 | ||||
-rw-r--r-- | arping.c | 1200 | ||||
-rw-r--r-- | clockdiff.c | 687 | ||||
-rw-r--r-- | doc/Makefile | 47 | ||||
-rw-r--r-- | doc/arping.sgml | 221 | ||||
-rw-r--r-- | doc/clockdiff.sgml | 161 | ||||
-rw-r--r-- | doc/docbook2man-spec.pl | 1164 | ||||
-rw-r--r-- | doc/index.db | 28 | ||||
-rw-r--r-- | doc/index.out | 87 | ||||
-rw-r--r-- | doc/iputils.db | 209 | ||||
-rw-r--r-- | doc/ninfod.sgml | 120 | ||||
-rw-r--r-- | doc/pg3.sgml | 175 | ||||
-rw-r--r-- | doc/ping.sgml | 683 | ||||
-rw-r--r-- | doc/rarpd.sgml | 170 | ||||
-rw-r--r-- | doc/rdisc.sgml | 246 | ||||
-rw-r--r-- | doc/snapshot.db | 1 | ||||
-rw-r--r-- | doc/tftpd.sgml | 151 | ||||
-rw-r--r-- | doc/tracepath.sgml | 191 | ||||
-rw-r--r-- | doc/traceroute6.sgml | 97 | ||||
-rw-r--r-- | in6_flowlabel.h | 39 | ||||
-rwxr-xr-x | ipg | 34 | ||||
-rw-r--r-- | iputils.spec | 80 | ||||
-rw-r--r-- | ninfod/COPYING | 26 | ||||
-rw-r--r-- | ninfod/Makefile.in | 88 | ||||
-rw-r--r-- | ninfod/config.h.in | 136 | ||||
-rwxr-xr-x | ninfod/configure | 5422 | ||||
-rw-r--r-- | ninfod/configure.in | 137 | ||||
-rw-r--r-- | ninfod/icmp6_nodeinfo.h | 97 | ||||
-rwxr-xr-x | ninfod/install-sh | 251 | ||||
-rw-r--r-- | ninfod/ni_ifaddrs.c | 541 | ||||
-rw-r--r-- | ninfod/ni_ifaddrs.h | 45 | ||||
-rw-r--r-- | ninfod/ninfod.c | 729 | ||||
-rw-r--r-- | ninfod/ninfod.h | 136 | ||||
-rw-r--r-- | ninfod/ninfod.sh.in | 37 | ||||
-rw-r--r-- | ninfod/ninfod_addrs.c | 469 | ||||
-rw-r--r-- | ninfod/ninfod_core.c | 702 | ||||
-rw-r--r-- | ninfod/ninfod_name.c | 387 | ||||
-rw-r--r-- | ping.c | 1344 | ||||
-rw-r--r-- | ping6.c | 1827 | ||||
-rw-r--r-- | ping6_niquery.h | 49 | ||||
-rw-r--r-- | ping_common.c | 1062 | ||||
-rw-r--r-- | ping_common.h | 288 | ||||
-rw-r--r-- | rarpd.c | 724 | ||||
-rw-r--r-- | rdisc.c | 1528 | ||||
-rw-r--r-- | tftp.h | 89 | ||||
-rw-r--r-- | tftpd.c | 537 | ||||
-rw-r--r-- | tftpsubs.c | 251 | ||||
-rw-r--r-- | tracepath.c | 438 | ||||
-rw-r--r-- | tracepath6.c | 561 | ||||
-rw-r--r-- | traceroute6.c | 950 |
55 files changed, 26223 insertions, 0 deletions
@@ -0,0 +1,24 @@ + +make +make html +make man +lynx doc/iputils.html +Read... + + + +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 nevertheless "make" does not work, +delete iputils and go to sleep. The next day repeat. If even full reset +did not help, I bring apologies. :-) + + + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ae92f34 --- /dev/null +++ b/Makefile @@ -0,0 +1,211 @@ +# +# Configuration +# + +# CC +CC=gcc +# Path to parent kernel include files directory +LIBC_INCLUDE=/usr/include +# Libraries +ADDLIB= +# Linker flags +LDFLAG_STATIC=-Wl,-Bstatic +LDFLAG_DYNAMIC=-Wl,-Bdynamic + +# +# Options +# + +# Capability support (with libcap) +USE_CAP=yes +# sysfs support (with libsysfs - deprecated) +USE_SYSFS=no +# IDN support (experimental) +USE_IDN=no +# Do not use getifaddrs +WITHOUT_IFADDRS=no +# arping default device +ARPING_DEFAULT_DEVICE=eth0 +# rdisc server (-r option) support +ENABLE_RDISC_SERVER=no +# ping6 source routing (deprecated by RFC5095) +ENABLE_PING6_RTHDR=no + +# ------------------------------------- +# What a pity, all new gccs are buggy and -Werror does not work. Sigh. +# CCOPT=-fno-strict-aliasing -Wstrict-prototypes -Wall -Werror -g +CCOPT=-fno-strict-aliasing -Wstrict-prototypes -Wall -g +CCOPTOPT=-O3 +GLIBCFIX=-D_GNU_SOURCE +DEFINES= +LDLIB= + +ifneq ($(USE_CAP),no) + DEF_CAP = -DCAPABILITIES + LIB_CAP = -lcap +endif + +ifneq ($(USE_SYSFS),no) + DEF_SYSFS = -DUSE_SYSFS + LIB_SYSFS = -lsysfs +endif + +ifneq ($(USE_IDN),no) + DEF_IDN = -DUSE_IDN +ifeq ($(USE_IDN),static) + LIB_IDN = $(LDFLAG_STATIC) -lidn $(LDFLAG_DYNAMIC) +else + LIB_IDN = -lidn +endif +endif + +ifneq ($(WITHOUT_IFADDRS),no) + DEF_WITHOUT_IFADDRS = -DWITHOUT_IFADDRS +endif + +ifneq ($(ENABLE_RDISC_SERVER),no) + DEF_ENABLE_RDISC_SERVER = -DRDISC_SERVER +endif + +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 + +# ------------------------------------- +IPV4_TARGETS=tracepath ping clockdiff rdisc arping tftpd rarpd +IPV6_TARGETS=tracepath6 traceroute6 ping6 +TARGETS=$(IPV4_TARGETS) $(IPV6_TARGETS) + +CFLAGS=$(CCOPTOPT) $(CCOPT) $(GLIBCFIX) $(DEFINES) +LDLIBS=$(LDLIB) $(ADDLIB) + +LASTTAG:=`git describe HEAD | sed -e 's/-.*//'` +TAG:=`date +s%Y%m%d` + +# ------------------------------------- +.PHONY: all ninfod clean distclean man html check-kernel modules snapshot + +all: $(TARGETS) + +%.s: %.c + $(COMPILE.c) $< $(DEF_$(patsubst %.o,%,$@)) -S -o $@ +%.o: %.c + $(COMPILE.c) $< $(DEF_$(patsubst %.o,%,$@)) -o $@ +$(TARGETS): %: %.o + $(LINK.o) $^ $(LIB_$@) $(LDLIBS) -o $@ + +# ------------------------------------- +# arping +DEF_arping = $(DEF_SYSFS) $(DEF_CAP) $(DEF_IDN) $(DEF_WITHOUT_IFADDRS) -DDEFAULT_DEVICE=\"$(ARPING_DEFAULT_DEVICE)\" +LIB_arping = $(LIB_SYSFS) $(LIB_CAP) $(LIB_IDN) + +# clockdiff +DEF_clockdiff = $(DEF_CAP) +LIB_clockdiff = $(LIB_CAP) + +# ping / ping6 +DEF_ping_common = $(DEF_CAP) $(DEF_IDN) +DEF_ping = $(DEF_CAP) $(DEF_IDN) +LIB_ping = $(LIB_CAP) $(LIB_IDN) +DEF_ping6 = $(DEF_CAP) $(DEF_IDN) $(DEF_ENABLE_PING6_RTHDR) +LIB_ping6 = $(LIB_CAP) $(LIB_IDN) -lresolv -lcrypto + +ping: ping_common.o +ping6: ping_common.o +ping.o ping_common.o: ping_common.h +ping6.o: ping_common.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) + +# tracepath6 +DEF_tracepath6 = $(DEF_IDN) +LIB_tracepath6 = + +# 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 + +# ------------------------------------- +# modules / check-kernel are only for ancient kernels; obsolete +check-kernel: +ifeq ($(KERNEL_INCLUDE),) + @echo "Please, set correct KERNEL_INCLUDE"; false +else + @set -e; \ + if [ ! -r $(KERNEL_INCLUDE)/linux/autoconf.h ]; then \ + echo "Please, set correct KERNEL_INCLUDE"; false; fi +endif + +modules: check-kernel + $(MAKE) KERNEL_INCLUDE=$(KERNEL_INCLUDE) -C Modules + +# ------------------------------------- +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 + +# ------------------------------------- +snapshot: + @if [ "`uname -n`" != "pleiades" ]; then echo "Not authorized to advance snapshot"; exit 1; fi + @date "+[$(TAG)]" > RELNOTES.NEW + @echo >>RELNOTES.NEW + @git log --no-merges $(LASTTAG).. | git shortlog >> RELNOTES.NEW + @echo >> RELNOTES.NEW + @cat RELNOTES >> RELNOTES.NEW + @mv RELNOTES.NEW RELNOTES + @date "+static char SNAPSHOT[] = \"$(TAG)\";" > SNAPSHOT.h + @$(MAKE) -C doc snapshot + @$(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 + 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 <robert.olsson@its.uu.se> + * 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 <linux/module.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/types.h> +#include <linux/string.h> +#include <linux/ptrace.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/malloc.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/inet.h> +#include <asm/byteorder.h> +#include <asm/bitops.h> +#include <asm/io.h> +#include <asm/dma.h> + +#include <linux/in.h> +#include <linux/ip.h> +#include <linux/udp.h> +#include <linux/skbuff.h> +#include <linux/netdevice.h> +#include <linux/inetdevice.h> +#include <linux/rtnetlink.h> +#include <linux/proc_fs.h> +#include <linux/if_arp.h> +#include <net/checksum.h> + +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<maxlen;i++) { + switch(buffer[i]) { + case '\"': + case '\n': + case '\r': + case '\t': + case ' ': + case '=': + break; + default: + goto done; + } + } +done: + return i; +} + +unsigned long num_arg(const char *buffer, unsigned long maxlen, + unsigned long *num) +{ + int i=0; + *num = 0; + + for(; i<maxlen;i++) { + if( (buffer[i] >= '0') && (buffer[i] <= '9')) { + *num *= 10; + *num += buffer[i] -'0'; + } + else + break; + } + return i; +} + +int strn_len(const char *buffer, unsigned int maxlen) +{ + int i=0; + + for(; i<maxlen;i++) + switch(buffer[i]) { + case '\"': + case '\n': + case '\r': + case '\t': + case ' ': + goto done_str; + default: + } +done_str: + return i; +} + +int proc_pg_write(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + int i=0, max, len; + char name[16], valstr[32]; + unsigned long value = 0; + + if (count < 1) { + sprintf(pg_result, "Wrong command format"); + return -EINVAL; + } + + max = count -i; + i += count_trail_chars(&buffer[i], max); + + /* Read variable name */ + + len = strn_len(&buffer[i], sizeof(name)-1); + memset(name, 0, sizeof(name)); + strncpy(name, &buffer[i], len); + i += len; + + max = count -i; + len = count_trail_chars(&buffer[i], max); + i += len; + + if (debug) + printk("pg: %s,%lu\n", name, count); + + /* Only stop is allowed when we are running */ + + if(!strcmp(name, "stop")) { + forced_stop=1; + if (pg_busy) + strcpy(pg_result, "Stopping"); + return count; + } + + if (pg_busy) { + strcpy(pg_result, "Busy"); + return -EINVAL; + } + + if(!strcmp(name, "pkt_size")) { + len = num_arg(&buffer[i], 10, &value); + i += len; + if (value < 14+20+8) + value = 14+20+8; + pkt_size = value; + sprintf(pg_result, "OK: pkt_size=%u", pkt_size); + return count; + } + if(!strcmp(name, "frags")) { + len = num_arg(&buffer[i], 10, &value); + i += len; + nfrags = value; + sprintf(pg_result, "OK: frags=%u", nfrags); + return count; + } + if(!strcmp(name, "ipg")) { + len = num_arg(&buffer[i], 10, &value); + i += len; + pg_ipg = value; + sprintf(pg_result, "OK: ipg=%u", pg_ipg); + return count; + } + if(!strcmp(name, "count")) { + len = num_arg(&buffer[i], 10, &value); + i += len; + pg_count = value; + sprintf(pg_result, "OK: count=%u", pg_count); + return count; + } + if(!strcmp(name, "odev")) { + len = strn_len(&buffer[i], sizeof(pg_outdev)-1); + memset(pg_outdev, 0, sizeof(pg_outdev)); + strncpy(pg_outdev, &buffer[i], len); + i += len; + sprintf(pg_result, "OK: odev=%s", pg_outdev); + return count; + } + if(!strcmp(name, "dst")) { + len = strn_len(&buffer[i], sizeof(pg_dst)-1); + memset(pg_dst, 0, sizeof(pg_dst)); + strncpy(pg_dst, &buffer[i], len); + if(debug) + printk("pg: dst set to: %s\n", pg_dst); + i += len; + sprintf(pg_result, "OK: dst=%s", pg_dst); + return count; + } + if(!strcmp(name, "dstmac")) { + char *v = valstr; + unsigned char *m = pg_dstmac; + + len = strn_len(&buffer[i], sizeof(valstr)-1); + memset(valstr, 0, sizeof(valstr)); + strncpy(valstr, &buffer[i], len); + i += len; + + for(*m = 0;*v && m < pg_dstmac+6;v++) { + if(*v >= '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 <robert.olsson@its.uu.se"); +MODULE_DESCRIPTION("Packet Generator tool"); +MODULE_PARM(pg_count, "i"); +MODULE_PARM(pg_ipg, "i"); +MODULE_PARM(pg_cpu_speed, "i"); +#endif + +/* + * Local variables: + * compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/include -Wall -Wstrict-prototypes -O2 -c pg3.c" + * End: + */ diff --git a/RELNOTES b/RELNOTES new file mode 100644 index 0000000..8036d26 --- /dev/null +++ b/RELNOTES @@ -0,0 +1,598 @@ +[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 parenthese 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 <mmj@suse.de> Recursive citation: +"On request of Mads Martin Jørgensen <mmj@suse.de> 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 <jkc@redhat.com>. 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 <frodo@morgul.net> Fix to doc. +!* Noah L. Meyerhans <frodo@morgul.net> What is the problem with "long" triptime? +! Reporter does not respond. _Malignantly_. +* Thomas 'Dent' Mirlacher <dent@cosy.sbg.ac.at> Ping did not exit sometimes! +* Add option -W to override default 10 second linger timeout. Me. +* Mads Martin Jørgensen <mmj@suse.de>: 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 <pekkas@netcore.fi> Forgotten \n messing output of ping6. +* Noah L. Meyerhans <frodo@morgul.net> traceroute6 -q 1 did not work. +* Pekka Savola <pekkas@netcore.fi> various sizeof() cleanups in traceroute6.c +* "Dmitry V. Levin" <ldv@alt-linux.org> 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" <tilHeinrich@web.de> 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 <dax@gurulabs.com>: added _unsupported_ option to comppile + rdisc_srv. + +[020124] +* Michal Kochanowicz <michal@michal.waw.pl> typos in tracepath.8 +* Michael Wardle <michael.wardle@adacel.com>: 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 <utz.bacher@de.ibm.com> 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 <bakunin@maphiasoft.org> (:-)) + found mud in tftpd.c, it will crash when directory supplied in argument + is longer ~512 symbols. +* Alexandr D. Kanevskiy <kad@blackcatlinux.com>: buffer overflow + in clockdiff. Very stupid one, the overflowed buffer even was not used. :-) +* Alexandr D. Kanevskiy <kad@blackcatlinux.com>: 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 <yozh@mx1.ru>, tracepath/tracepth6 segfaulted when + used without address. +* Alexandr D. Kanevskiy <kad@blackcatlinux.com>: arping printed + "permission denied" instead of showing help page to non-superuser. + +[010824] +* Alexandr D. Kanevskiy <kad@blackcatlinux.com>: ping compiled + for linux-2.4 forgot to send the second packet, when used with linux-2.2 +* Chris Evans <chris@scary.beasts.org>: 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 <kad@blackcatlinux.com>: relayed patches + by Solar_Diz. Only missing description of option -q is accepted. +* <ipatel@wilnetonline.net> ping6 printed wrong mtu. +* Alexandr D. Kanevskiy <kad@blackcatlinux.com>: -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 <pekkas@netcore.fi> around setting tos bits. +* arping: broadcast-only mode by Ard van Breemen <ard@telegraafnet.nl> +* ping6/traceroute6: parse ICMP errors with extension headers (me) + traceroute6 works with size > mtu now. Nice. +* ping: Erik Quanstrom <quanstro@clark.net>. 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 <igloo@earth.li>, 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 <tommyl@zeroknowledge.com> +* ping: audible ping by Patrik Schilt <patrik@bnc.ch> + 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 <pekkas@netcore.fi> +* tracepath* DNS names. By Pawel Krawczyk <kravietz@ceti.com.pl> and + Arkadiusz Miskiewicz <misiek@pld.org.pl> +* 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 <chris@ferret.lmh.ox.ac.uk> + - 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 <pekkas@netcore.fi> + - 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 <chris@ferret.lmh.ox.ac.uk> + - 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 <damjan.lango@hermes.si>) +* iputils man and help updates. Pekka Savola <Pekka.Savola@netcore.fi> +* ping.8, fix to ping man page. By Dadid Eisner <cradle@glue.umd.edu> +* ping prints addresses in numeric, if destination is numeric. + Proposed by Tim Waugh <twaugh@meme.surrey.redhat.com> + +New: +* ping: strncpy bug <typo@inferno.tusculum.edu> +* arping: improvements by Charles Howes <croot@micro-logistics.com> + - 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 <sfarin@ratol.fi>) + +[000404] +* tracepath*, "NURDUnet-gw" bug workaround. + (noticed by Vitaly E.Lavrov <lve@aanet.ru>) +* tracepath*, handle case of routers initializing rtt to 128. + Vitaly E.Lavrov <lve@aanet.ru> +* shadowed icmp_sock in ping6. James Morris <jmorris@@intercode.com.au> +* 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 <jbj@redhat.com> +* 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 <marc@mbsi.ca> +* 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 <oms@cp.tomsk.su>) +* 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..4b934a0 --- /dev/null +++ b/SNAPSHOT.h @@ -0,0 +1 @@ +static char SNAPSHOT[] = "s20121126"; diff --git a/arping.c b/arping.c new file mode 100644 index 0000000..f6433c2 --- /dev/null +++ b/arping.c @@ -0,0 +1,1200 @@ +/* + * 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, <kuznet@ms2.inr.ac.ru> + * YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org> + */ + +#include <stdlib.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <linux/sockios.h> +#include <sys/file.h> +#include <sys/time.h> +#include <sys/signal.h> +#include <sys/ioctl.h> +#include <net/if.h> +#include <linux/if_packet.h> +#include <linux/if_ether.h> +#include <net/if_arp.h> +#include <sys/uio.h> +#ifdef CAPABILITIES +#include <sys/prctl.h> +#include <sys/capability.h> +#endif + +#include <netdb.h> +#include <unistd.h> +#include <stdio.h> +#include <ctype.h> +#include <errno.h> +#include <string.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#ifdef USE_SYSFS +#include <sysfs/libsysfs.h> +struct sysf_devattr_values; +#endif + +#ifndef WITHOUT_IFADDRS +#include <ifaddrs.h> +#endif + +#ifdef USE_IDN +#include <idna.h> +#include <locale.h> +#endif + +#include "SNAPSHOT.h" + +static void usage(void) __attribute__((noreturn)); + +#ifdef DEFAULT_DEVICE +# define DEFAULT_DEVICE_STR DEFAULT_DEVICE +#else +# define DEFAULT_DEVICE_STR "no default" +# 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 timeval 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 (" DEFAULT_DEVICE_STR ")\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, }; +#endif + +void limit_capabilities(void) +{ +#ifdef CAPABILITIES + cap_t cap_p; + + cap_p = cap_init(); + if (!cap_p) { + perror("arping: cap_init"); + exit(-1); + } + + if (cap_set_flag(cap_p, CAP_PERMITTED, 1, caps, CAP_SET) < 0) { + perror("arping: cap_set_flag"); + exit(-1); + } + + 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); + } + + if (cap_free(cap_p) < 0) { + perror("arping: cap_free"); + exit(-1); + } +#else + euid = geteuid(); +#endif +} + +int modify_capability_raw(int on) +{ +#ifdef CAPABILITIES + cap_t cap_p; + + cap_p = cap_get_proc(); + if (!cap_p) { + perror("arping: cap_get_proc"); + return -1; + } + + if (cap_set_flag(cap_p, CAP_EFFECTIVE, 1, caps, on ? CAP_SET : CAP_CLEAR) < 0) { + perror("arping: cap_set_flag"); + return -1; + } + + if (cap_set_proc(cap_p) < 0) { + perror("arping: cap_set_proc"); + return -1; + } + + if (cap_free(cap_p) < 0) { + perror("arping: cap_free"); + return -1; + } +#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); + } + + if (cap_free(cap_p) < 0) { + perror("arping: cap_free"); + exit(-1); + } +#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 timeval 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; + + gettimeofday(&now, NULL); + 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); +} + +void catcher(void) +{ + struct timeval tv, tv_s, tv_o; + + gettimeofday(&tv, NULL); + + if (start.tv_sec==0) + start = tv; + + timersub(&tv, &start, &tv_s); + tv_o.tv_sec = timeout; + tv_o.tv_usec = 500 * 1000; + + if (count-- == 0 || (timeout && timercmp(&tv_s, &tv_o, >))) + finish(); + + timersub(&tv, &last, &tv_s); + tv_o.tv_sec = 0; + + if (last.tv_sec==0 || timercmp(&tv_s, &tv_o, >)) { + send_pack(s, src, dst, + (struct sockaddr_ll *)&me, (struct sockaddr_ll *)&he); + if (count == 0 && unsolicited) + finish(); + } + alarm(1); +} + +void print_hex(unsigned char *p, int len) +{ + int i; + for (i=0; i<len; i++) { + printf("%02X", p[i]); + if (i != len-1) + printf(":"); + } +} + +int recv_pack(unsigned char *buf, int len, struct sockaddr_ll *FROM) +{ + struct timeval tv; + struct arphdr *ah = (struct arphdr*)buf; + unsigned char *p = (unsigned char *)(ah+1); + struct in_addr src_ip, dst_ip; + + gettimeofday(&tv, NULL); + + /* Filter out wild packets */ + if (FROM->sll_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 = <unspec> + + 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 = (tv.tv_sec-last.tv_sec) * 1000000 + + tv.tv_usec-last.tv_usec; + long msecs = (usecs+500)/1000; + usecs -= msecs*1000 - 500; + printf(" %ld.%03ldms\n", msecs, usecs); + } else { + printf(" UNSOLICITED?\n"); + } + fflush(stdout); + } + received++; + if (FROM->sll_pkttype != PACKET_HOST) + brd_recv++; + if (ah->ar_op == htons(ARPOP_REQUEST)) + req_recv++; + if (quit_on_reply) + 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, +#if 0 + SYSFS_DEVATTR_TYPE, + SYSFS_DEVATTR_ADDRESS, +#endif + 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, + }, +#if 0 + [SYSFS_DEVATTR_TYPE] = { + .name = "type", + .handler = sysfs_devattr_ulong_dec, + }, + [SYSFS_DEVATTR_ADDRESS] = { + .name = "address", + .handler = sysfs_devattr_macaddr, + .free = 1, + }, +#endif + [SYSFS_DEVATTR_BROADCAST] = { + .name = "broadcast", + .handler = sysfs_devattr_macaddr, + .free = 1, + }, +}; +#endif + +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; + + 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; + + if (device.ifa) { + if (device.ifa->ifa_flags & IFF_RUNNING) + continue; + } + + device.ifa = ifa; + + if (ifa->ifa_flags & IFF_RUNNING) + break; + } + + if (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; + *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) +{ +#ifdef USE_SYSFS + struct sysfs_class *cls_net; + struct dlist *dev_list; + struct sysfs_class_device *dev; + struct sysfs_attribute *dev_attr; + struct sysfs_devattr_values sysfs_devattr_values; + + if (!device.sysfs) { + device.sysfs = malloc(sizeof(*device.sysfs)); + sysfs_devattr_values_init(device.sysfs, 0); + } + + cls_net = sysfs_open_class("net"); + if (!cls_net) { + perror("sysfs_open_class"); + goto out; + } + + dev_list = sysfs_get_class_devices(cls_net); + if (!dev_list) { + perror("sysfs_get_class_devices"); + goto out; + } + + sysfs_devattr_values_init(&sysfs_devattr_values, 0); + + dlist_for_each_data(dev_list, dev, struct sysfs_class_device) { + int i; + int rc = -1; + + if (device.name && strcmp(dev->name, device.name)) + goto do_next; + + sysfs_devattr_values_init(&sysfs_devattr_values, 1); + + for (i = 0; i < SYSFS_DEVATTR_NUM; i++) { + + dev_attr = sysfs_get_classdev_attr(dev, sysfs_devattrs[i].name); + if (!dev_attr) { + perror("sysfs_get_classdev_attr"); + rc = -1; + break; + } + if (sysfs_read_attribute(dev_attr)) { + perror("sysfs_read_attribute"); + rc = -1; + break; + } + rc = sysfs_devattrs[i].handler(dev_attr->value, &sysfs_devattr_values, i); + + //sysfs_close_attribute(dev_attr); + + 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(dev->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 (device.sysfs->value[SYSFS_DEVATTR_FLAGS].ulong & IFF_RUNNING) + break; + + continue; +do_next: + sysfs_devattr_values_init(&sysfs_devattr_values, 1); + } + + //sysfs_close_list(dev_list); + sysfs_close_class(cls_net); + + device.ifindex = device.sysfs->value[SYSFS_DEVATTR_IFINDEX].ulong; + device.name = device.sysfs->ifname; + + return 0; +out: +#endif + return -1; +} + +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; + + 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; + } 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++) { + memcpy(&ifrbuf.ifr_name, ifr->ifr_name, sizeof(ifrbuf.ifr_name)); + if (check_device_by_ioctl(s, &ifrbuf)) + continue; + break; + } + } + + close(s); + + 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; +} + +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: No device found; device (option -I) is required.\n"); + usage(); + } + + if (inet_aton(target, &dst) != 1) { + struct hostent *hp; + char *idn = target; +#ifdef USE_IDN + int rc; + + rc = idna_to_ascii_lz(target, &idn, 0); + + if (rc != IDNA_SUCCESS) { + fprintf(stderr, "arping: IDN encoding failed: %s\n", idna_strerror(rc)); + exit(2); + } +#endif + + hp = gethostbyname2(idn, AF_INET); + if (!hp) { + fprintf(stderr, "arping: unknown host %s\n", target); + exit(2); + } + +#ifdef USE_IDN + free(idn); +#endif + + memcpy(&dst, hp->h_addr, 4); + } + + 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; + + if ((cc = recvfrom(s, packet, sizeof(packet), 0, + (struct sockaddr *)&from, &alen)) < 0) { + perror("arping: recvfrom"); + continue; + } + + sigemptyset(&sset); + sigaddset(&sset, SIGALRM); + sigaddset(&sset, SIGINT); + sigprocmask(SIG_BLOCK, &sset, &osset); + recv_pack(packet, cc, (struct sockaddr_ll *)&from); + sigprocmask(SIG_SETMASK, &osset, NULL); + } +} + + diff --git a/clockdiff.c b/clockdiff.c new file mode 100644 index 0000000..7c1ea1b --- /dev/null +++ b/clockdiff.c @@ -0,0 +1,687 @@ +#include <time.h> +#include <sys/types.h> +#include <sys/param.h> +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <math.h> +#include <string.h> +#include <sys/time.h> +#include <sys/timex.h> +#include <errno.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/ip_icmp.h> +#define TSPTYPES +#include <protocols/timed.h> +#include <fcntl.h> +#include <netdb.h> +#include <arpa/inet.h> +#include <errno.h> +#include <linux/types.h> +#ifdef CAPABILITIES +#include <sys/capability.h> +#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(u_short *addr, int len) +{ + union word { + char c[2]; + u_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] = *(u_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 u_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; + u_char packet[PACKET_IN], opacket[64]; + struct icmphdr *icp = (struct icmphdr *) packet; + 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); + msgcount = 0; + + 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((u_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; + u_char packet[PACKET_IN], opacket[64]; + struct icmphdr *icp = (struct icmphdr *) packet; + 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); + msgcount = 0; + + 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((u_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] <host>\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 hostent * hp; + 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)); + hp = gethostbyname(hostname); + if (hp == NULL) { + fprintf(stderr, "clockdiff: %s: my host not found\n", hostname); + exit(1); + } + myname = strdup(hp->h_name); + + hp = gethostbyname(argv[1]); + if (hp == NULL) { + fprintf(stderr, "clockdiff: %s: host not found\n", argv[1]); + exit(1); + } + hisname = strdup(hp->h_name); + + memset(&server, 0, sizeof(server)); + server.sin_family = hp->h_addrtype; + memcpy(&(server.sin_addr.s_addr), hp->h_addr, 4); + + 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/Makefile b/doc/Makefile new file mode 100644 index 0000000..7ec4f1c --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,47 @@ +SGMLFILES=$(shell echo *.sgml) +HTMLFILES=$(subst .sgml,.html,$(SGMLFILES)) index.html +MANFILES=$(subst .sgml,.8,$(SGMLFILES)) + +all: html + +html: $(HTMLFILES) iputils.html + +man: $(MANFILES) + +# docbook scripts are incredibly dirty in the sense that they leak +# lots of some strange temporary junk directories and files. +# So, scope it to a temporary dir and clean all after each run. + +$(HTMLFILES): index.db + @-rm -rf tmp.db2html + @mkdir tmp.db2html + @set -e; cd tmp.db2html; docbook2html ../$< ; mv *.html .. + @-rm -rf tmp.db2html + +iputils.html: iputils.db + @-rm -rf tmp.db2html + @mkdir tmp.db2html + @set -e; cd tmp.db2html; docbook2html -u -o html ../$< ; mv html/$@ .. + @-rm -rf tmp.db2html + +# docbook2man produces utterly ugly output and I did not find +# any way to customize this but hacking backend perl script a little. +# Well, hence... + +$(MANFILES): index.db + @-mkdir tmp.db2man + @set -e; cd tmp.db2man; nsgmls ../$< | sgmlspl ../docbook2man-spec.pl ; mv $@ .. + @-rm -rf tmp.db2man + +clean: + @rm -rf $(MANFILES) $(HTMLFILES) iputils.html tmp.db2html tmp.db2man + +snapshot: + @date "+%y%m%d" > snapshot.db + + +$(MANFILES): $(SGMLFILES) + +$(HTMLFILES): $(SGMLFILES) + + diff --git a/doc/arping.sgml b/doc/arping.sgml new file mode 100644 index 0000000..0688ecb --- /dev/null +++ b/doc/arping.sgml @@ -0,0 +1,221 @@ +<refentry id="arping"> + +<refmeta> +<refentrytitle>arping</refentrytitle> +<manvolnum>8</manvolnum> +<refmiscinfo>iputils-&snapshot;</refmiscinfo> +</refmeta> + + +<refnamediv> +<refname>arping</refname> +<refpurpose>send ARP REQUEST to a neighbour host</refpurpose> +</refnamediv> + +<refsynopsisdiv> +<cmdsynopsis> +<command>arping</command> +<arg choice="opt"><option>-AbDfhqUV</option></arg> +<arg choice="opt">-c <replaceable/count/</arg> +<arg choice="opt">-w <replaceable/deadline/</arg> +<arg choice="opt">-s <replaceable/source/</arg> +<arg choice="req">-I <replaceable/interface/</arg> +<arg choice="req"><replaceable/destination/</arg> +</cmdsynopsis> +</refsynopsisdiv> + +<refsect1><title>DESCRIPTION</title> +<para> +Ping <replaceable/destination/ on device <replaceable/interface/ by ARP packets, +using source address <replaceable/source/. +</para> +</refsect1> + +<refsect1><title>OPTIONS</title> + +<variablelist> + + <varlistentry> + <term><option/-A/</term> + <listitem><para> +The same as <option/-U/, but ARP REPLY packets used instead +of ARP REQUEST. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>-b</option></term> + <listitem><para> +Send only MAC level broadcasts. Normally <command/arping/ starts +from sending broadcast, and switch to unicast after reply received. + </para></listitem> + </varlistentry> + + + <varlistentry> + <term><option><anchor id="arping.count">-c <replaceable/count/</option></term> + <listitem><para> +Stop after sending <replaceable/count/ ARP REQUEST +packets. With +<link linkend="arping.deadline"><replaceable/deadline/</link> +option, <command/arping/ waits for +<replaceable/count/ ARP REPLY packets, until the timeout expires. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option/-D/</term> + <listitem><para> +Duplicate address detection mode (DAD). See +<ulink url="http://tools.ietf.org/rfc/rfc2131.txt">RFC2131, 4.4.1</ulink>. +Returns 0, if DAD succeeded i.e. no replies are received + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>-f</option></term> + <listitem><para> +Finish after the first reply confirming that target is alive. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option><anchor id="opt.interface">-I <replaceable/interface/</option></term> + <listitem><para> +Name of network device where to send ARP REQUEST packets. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>-h</option></term> + <listitem><para> +Print help page and exit. + </para></listitem> + </varlistentry> + + + <varlistentry> + <term><option/-q/</term> + <listitem><para> +Quiet output. Nothing is displayed. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option><anchor id="opt.source">-s <replaceable/source/</option></term> + <listitem><para> +IP source address to use in ARP packets. +If this option is absent, source address is: + <itemizedlist> + <listitem><para> +In DAD mode (with option <option/-D/) set to 0.0.0.0. + </para></listitem> + <listitem><para> +In Unsolicited ARP mode (with options <option/-U/ or <option/-A/) +set to <replaceable/destination/. + </para></listitem> + <listitem><para> +Otherwise, it is calculated from routing tables. + </para></listitem> + </itemizedlist> + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option/-U/</term> + <listitem><para> +Unsolicited ARP mode to update neighbours' ARP caches. +No replies are expected. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>-V</option></term> + <listitem><para> +Print version of the program and exit. + </para></listitem> + </varlistentry> + + + <varlistentry> + <term><option><anchor id="arping.deadline">-w <replaceable/deadline/</option></term> + <listitem><para> +Specify a timeout, in seconds, before +<command/arping/ +exits regardless of how many +packets have been sent or received. In this case +<command/arping/ +does not stop after +<link linkend="arping.count"><replaceable/count/</link> +packet are sent, it waits either for +<link linkend="arping.deadline"><replaceable/deadline/</link> +expire or until +<link linkend="arping.count"><replaceable/count/</link> +probes are answered. + </para></listitem> + </varlistentry> +</variablelist> +</refsect1> + +<refsect1><title>SEE ALSO</title> +<para> +<link linkend="ping"> +<citerefentry><refentrytitle/ping/<manvolnum/8/</citerefentry></link>, +<link linkend="clockdiff"> +<citerefentry><refentrytitle/clockdiff/<manvolnum/8/</citerefentry></link>, +<link linkend="tracepath"> +<citerefentry><refentrytitle/tracepath/<manvolnum/8/</citerefentry></link>. +</para> +</refsect1> + +<refsect1><title>AUTHOR</title> +<para> +<command/arping/ was written by +<ulink url="mailto:kuznet@ms2.inr.ac.ru">Alexey Kuznetsov +<kuznet@ms2.inr.ac.ru></ulink>. +It is now maintained by +<ulink url="mailto:yoshfuji@skbuff.net">YOSHIFUJI Hideaki +<yoshfuji@skbuff.net></ulink>. +</para> +</refsect1> + +<refsect1><title>SECURITY</title> +<para> +<command/arping/ requires <constant/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. +</para> +</refsect1> + +<refsect1><title>AVAILABILITY</title> +<para> +<command/arping/ is part of <filename/iputils/ package +and the latest versions are available in source form at +<ulink url="http://www.skbuff.net/iputils/iputils-current.tar.bz2"> +http://www.skbuff.net/iputils/iputils-current.tar.bz2</ulink>. +</para> +</refsect1> + +<![IGNORE[ +<refsect1><title>COPYING</title> +<para> +<literallayout> +This documentation is free software; you can redistribute +it and/or modify it under the terms of the GNU General Public +License Version 2. + +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. + +For more details see the file COPYING in the source +distribution of Linux kernel of version 2.4. +</literallayout> +</para> +</refsect1> +]]> + + + +</refentry> diff --git a/doc/clockdiff.sgml b/doc/clockdiff.sgml new file mode 100644 index 0000000..e5ab592 --- /dev/null +++ b/doc/clockdiff.sgml @@ -0,0 +1,161 @@ +<refentry id="clockdiff"> + +<refmeta> +<refentrytitle>clockdiff</refentrytitle> +<manvolnum>8</manvolnum> +<refmiscinfo>iputils-&snapshot;</refmiscinfo> +</refmeta> + +<refnamediv> +<refname>clockdiff</refname> +<refpurpose>measure clock difference between hosts</refpurpose> +</refnamediv> + +<refsynopsisdiv> +<cmdsynopsis> +<command>clockdiff</command> +<arg choice="opt"><option>-o</option></arg> +<arg choice="opt"><option>-o1</option></arg> +<arg choice="req"><replaceable/destination/</arg> +</cmdsynopsis> +</refsynopsisdiv> + +<refsect1><title>DESCRIPTION</title> +<para> +<command/clockdiff/ Measures clock difference between us and +<replaceable/destination/ with 1 msec resolution using ICMP TIMESTAMP +<link linkend="clockdiff.icmp-timestamp">[2]</link> +packets or, optionally, IP TIMESTAMP option +<link linkend="clockdiff.ip-timestamp">[3]</link> +option added to ICMP ECHO. +<link linkend="clockdiff.icmp-echo">[1]</link> +</para> +</refsect1> + +<refsect1><title>OPTIONS</title> + +<variablelist> + + <varlistentry> + <term><option/-o/</term> + <listitem><para> +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). + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option/-o1/</term> + <listitem><para> +Slightly different form of <option/-o/, 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, +<option/-o/ is better for Linux. + </para></listitem> + </varlistentry> + +</variablelist> + +</refsect1> + +<refsect1><title>WARNINGS</title> +<itemizedlist> + <listitem><para> +Some nodes (Cisco) use non-standard timestamps, which is allowed +by RFC, but makes timestamps mostly useless. + </para></listitem> + <listitem><para> +Some nodes generate messed timestamps (Solaris>2.4), when +run <command/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. + </para></listitem> + <listitem><para> +<command/clockdiff/ shows difference in time modulo 24 days. + </para></listitem> +</itemizedlist> + +</refsect1> + +<refsect1><title>SEE ALSO</title> +<para> +<link linkend="ping"> +<citerefentry><refentrytitle/ping/<manvolnum/8/</citerefentry></link>, +<link linkend="arping"> +<citerefentry><refentrytitle/arping/<manvolnum/8/</citerefentry></link>, +<link linkend="tracepath"> +<citerefentry><refentrytitle/tracepath/<manvolnum/8/</citerefentry></link>. +</para> +</refsect1> + +<refsect1><title>REFERENCES</title> +<para> +[1] <anchor id="clockdiff.icmp-echo">ICMP ECHO, +<ulink url="http://tools.ietf.org/rfc/rfc792.txt"> +RFC0792, page 14</ulink>. +</para> +<para> +[2] <anchor id="clockdiff.icmp-timestamp">ICMP TIMESTAMP, +<ulink url="http://tools.ietf.org/rfc/rfc792.txt"> +RFC0792, page 16</ulink>. +</para> +<para> +[3] <anchor id="clockdiff.ip-timestamp">IP TIMESTAMP option, +<ulink url="http://tools.ietf.org/rfc/rfc791.txt"> +RFC0791, 3.1, page 16</ulink>. +</para> +</refsect1> + +<refsect1><title>AUTHOR</title> +<para> +<command/clockdiff/ was compiled by +<ulink url="mailto:kuznet@ms2.inr.ac.ru">Alexey Kuznetsov +<kuznet@ms2.inr.ac.ru></ulink>. It was based on code borrowed +from BSD <command/timed/ daemon. +It is now maintained by +<ulink url="mailto:yoshfuji@skbuff.net">YOSHIFUJI Hideaki +<yoshfuji@skbuff.net></ulink>. +</para> +</refsect1> + +<refsect1><title>SECURITY</title> +<para> +<command/clockdiff/ requires <constant/CAP_NET_RAW/ capability +to be executed. It is safe to be used as set-uid root. +</para> +</refsect1> + +<refsect1><title>AVAILABILITY</title> +<para> +<command/clockdiff/ is part of <filename/iputils/ package +and the latest versions are available in source form at +<ulink url="http://www.skbuff.net/iputils/iputils-current.tar.bz2"> +http://www.skbuff.net/iputils/iputils-current.tar.bz2</ulink>. +</para> +</refsect1> + +<![IGNORE[ +<refsect1><title>COPYING</title> +<para> +<literallayout> +This documentation is free software; you can redistribute +it and/or modify it under the terms of the GNU General Public +License Version 2. + +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. + +For more details see the file COPYING in the source +distribution of Linux kernel of version 2.4. +</literallayout> +</para> +</refsect1> +]]> + + + +</refentry> diff --git a/doc/docbook2man-spec.pl b/doc/docbook2man-spec.pl new file mode 100644 index 0000000..16ef7bc --- /dev/null +++ b/doc/docbook2man-spec.pl @@ -0,0 +1,1164 @@ +=head1 NAME + +docbook2man-spec - convert DocBook RefEntries to Unix manpages + +=head1 SYNOPSIS + +The SGMLSpm package from CPAN. This contains the sgmlspl script which +is used to grok this file. Use it like this: + +nsgmls some-docbook-document.sgml | sgmlspl docbook2man-spec.pl + +=head1 DESCRIPTION + +This is a sgmlspl spec file that produces Unix-style +manpages from RefEntry markup. + +See the accompanying RefEntry man page for 'plain new' documentation. :) + +=head1 LIMITATIONS + +Trying docbook2man on non-DocBook or non-conformant SGML results in +undefined behavior. :-) + +This program is a slow, dodgy Perl script. + +This program does not come close to supporting all the possible markup +in DocBook, and will produce wrong output in some cases with supported +markup. + +=head1 TODO + +Add new element handling and fix existing handling. Be robust. +Produce cleanest, readable man output as possible (unlike some +other converters). Follow Linux man(7) convention. +If this results in added logic in this script, +that's okay. The code should still be reasonably organized. + +Make it faster. If Perl sucks port it to another language. + +=head1 COPYRIGHT + +Copyright (C) 1998-1999 Steve Cheng <steve@ggi-project.org> + +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, or (at your option) any later +version. + +You should have received a copy of the GNU General Public License along with +this program; see the file COPYING. If not, please write to the Free +Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + +=cut + +# $Id: docbook2man-spec.pl,v 1.1 2000/07/21 20:22:30 rosalia Exp $ + +use SGMLS; # Use the SGMLS package. +use SGMLS::Output; # Use stack-based output. +use SGMLS::Refs; + +######################################################################## +# SGMLSPL script produced automatically by the script sgmlspl.pl +# +# Document Type: any, but processes only RefEntries +# Edited by: me :) +######################################################################## + +$write_manpages = 0; +$blank_xrefs = 0; + +sgml('start', sub { + push_output('nul'); + $raw_cdata = 1; # Makes it a bit faster. + + # Links file + open(LINKSFILE, ">manpage.links"); + + $Refs = new SGMLS::Refs("manpage.refs"); +}); +sgml('end', sub { + close(LINKSFILE); + if($blank_xrefs) { + print STDERR "Warning: output contains unresolved XRefs\n"; + } +}); + + + + +######################################################################## +# +# Output helpers +# +######################################################################## + +# Our own version of sgml() and output() to allow simple string output +# to play well with roff's stupid whitespace rules. + +sub man_sgml +{ + if(ref($_[1]) eq 'CODE') { + return &sgml; + } + + my $s = $_[1]; + + $s =~ s/\\/\\\\/g; + $s =~ s/'/\\'/g; + + # \n at the beginning means start at beginning of line + if($s =~ s/^\n//) { + $sub = 'sub { output "\n" unless $newline_last++; '; + if($s eq '') { + sgml($_[0], eval('sub { output "\n" unless $newline_last++; }')); + } elsif($s =~ /\n$/) { + sgml($_[0], eval("sub { output \"\\n\" unless \$newline_last++; output '$s'; }")); + } else { + sgml($_[0], eval("sub { output \"\\n\" unless \$newline_last; output '$s'; \$newline_last = 0; }")); + } + } else { + if($s =~ /\n$/) { + sgml($_[0], eval("sub { output '$s'; \$newline_last = 1; }")); + } else { + sgml($_[0], eval("sub { output '$s'; \$newline_last = 0; }")); + } + } +} + +sub man_output +{ + $_ = shift; + if(s/^\n//) { + output "\n" unless $newline_last++; + } + return if $_ eq ''; + + output $_; + + if(@_) { + output @_; + $newline_last = (pop(@_) =~ /\n$/); + } else { + $newline_last = ($_ =~ /\n$/) + } +} + +# Fold lines into one, quote some characters +sub fold_string +{ + $_ = shift; + + s/\\/\\\\/g; + s/"/\\\&"/g; + + # Change tabs to spaces + tr/\t\n/ /; + + # Trim whitespace from beginning and end. + s/^ +//; + s/ +$//; + + return $_; +} + +sub save_cdata() +{ + $raw_cdata++; + push_output('string'); +} + +sub bold_on() +{ + # If the last font is also bold, don't change anything. + # Basically this is to just get more readable man output. + if($fontstack[$#fontstack] ne 'bold') { + if(!$raw_cdata) { + output '\fB'; + $newline_last = 0; + } + } + push(@fontstack, 'bold'); +} + +sub italic_on() +{ + # If the last font is also italic, don't change anything. + if($fontstack[$#fontstack] ne 'italic') { + if(!$raw_cdata) { + output '\fI'; + $newline_last = 0; + } + } + push(@fontstack, 'italic'); +} + +sub font_off() +{ + my $thisfont = pop(@fontstack); + my $lastfont = $fontstack[$#fontstack]; + + # Only output font change if it is different + if($thisfont ne $lastfont) { + if($raw_cdata) { return; } + elsif($lastfont eq 'bold') { output '\fB'; } + elsif($lastfont eq 'italic') { output '\fI'; } + else { output '\fR'; } + + $newline_last = 0; + } +} + + + + + + +######################################################################## +# +# Manpage management +# +######################################################################## + +sgml('<REFENTRY>', sub { + # This will be overwritten at end of REFMETA, when we know the name of the page. + pop_output(); + + $write_manpages = 1; # Currently writing manpage. + + $nocollapse_whitespace = 0; # Current whitespace collapse counter. + $newline_last = 1; # At beginning of line? + # Just a bit of warning, you will see this variable manipulated + # manually a lot. It makes the code harder to follow but it + # saves you from having to worry about collapsing at the end of + # parse, stopping at verbatims, etc. + $raw_cdata = 0; # Instructs certain output functions to + # leave CDATA alone, so we can assign + # it to a string and process it, etc. + @fontstack = (); # Fonts being activated. + + $manpage_title = ''; # Needed for indexing. + $manpage_sect = ''; + @manpage_names = (); + + $manpage_misc = ''; + + $list_nestlevel = 0; # Indent certain nested content. +}); +sgml('</REFENTRY>', sub { + if(!$newline_last) { + output "\n"; + } + + $write_manpages = 0; + $raw_cdata = 1; + push_output('nul'); +}); + +sgml('</REFMETA>', sub { + push_output('file', "$manpage_title.$manpage_sect"); + + output <<_END_BANNER; +.\\" This manpage has been automatically generated by docbook2man +.\\" from a DocBook document. This tool can be found at: +.\\" <http://shell.ipoline.com/~elmert/comp/docbook2X/> +.\\" Please send any bug reports, improvements, comments, patches, +.\\" etc. to Steve Cheng <steve\@ggi-project.org>. +_END_BANNER + + my $manpage_date = `date "+%d %B %Y"`; + + output '.TH "'; + + # If the title is not mixed-case, convention says to + # uppercase the whole title. (The canonical title is + # lowercase.) + if($manpage_title =~ /[A-Z]/) { + output fold_string($manpage_title); + } else { + output uc(fold_string($manpage_title)); + } + + output '" "', fold_string($manpage_sect), + '" "', fold_string(`date "+%d %B %Y"`), + '" "', $manpage_misc, + '" "', $manpage_manual, + "\"\n"; + + $newline_last = 1; + + # References to this RefEntry. + my $id = $_[0]->parent->attribute('ID')->value; + if($id ne '') { + # The 'package name' part of the section should + # not be used when citing it. + my ($sectnum) = ($manpage_sect =~ /([0-9]*)/); + + if($_[0]->parent->attribute('XREFLABEL')->value eq '') { + $Refs->put("refentry:$id", "$manpage_title($sectnum)"); + } else { + $Refs->put("refentry:$id", + $_[0]->parent->attribute('XREFLABEL')->value . + "($sectnum)"); + } + } +}); + +sgml('<REFENTRYTITLE>', sub { + if($_[0]->in('REFMETA')) { + save_cdata(); + } else { + # Manpage citations are in bold. + bold_on(); + } +}); +sgml('</REFENTRYTITLE>', sub { + if($_[0]->in('REFMETA')) { + $raw_cdata--; + $manpage_title = pop_output(); + } + else { font_off(); } +}); + +sgml('<MANVOLNUM>', sub { + if($_[0]->in('REFMETA')) { + save_cdata(); + } else { + # Manpage citations use (). + output '('; + } +}); +sgml('</MANVOLNUM>', sub { + if($_[0]->in('REFMETA')) { + $raw_cdata--; + $manpage_sect = pop_output(); + } + else { output ')' } +}); + +sgml('<REFMISCINFO>', \&save_cdata); +sgml('</REFMISCINFO>', sub { + $raw_cdata--; + $manpage_misc = fold_string(pop_output()); +}); + + +# NAME section +man_sgml('<REFNAMEDIV>', "\n.SH NAME\n"); + +sgml('<REFNAME>', \&save_cdata); +sgml('</REFNAME>', sub { + $raw_cdata--; + push(@manpage_names, pop_output()); +}); + +sgml('<REFPURPOSE>', \&save_cdata); +sgml('</REFPURPOSE>', sub { + $raw_cdata--; + my $manpage_purpose = fold_string(pop_output()); + + for(my $i = 0; $i < $#manpage_names; $i++) { + output fold_string($manpage_names[$i]), ', '; + } + + output fold_string($manpage_names[$#manpage_names]); + output " \\- $manpage_purpose\n"; + + $newline_last = 1; + + foreach(@manpage_names) { + # Don't link to itself + if($_ ne $manpage_title) { + print LINKSFILE "$manpage_title.$manpage_sect $_.$manpage_sect\n"; + } + } +}); + +man_sgml('<REFCLASS>', "\n.sp\n"); + +#RefDescriptor + + + + + +######################################################################## +# +# SYNOPSIS section and synopses +# +######################################################################## + +man_sgml('<REFSYNOPSISDIV>', "\n.SH SYNOPSIS\n"); +man_sgml('</REFSYNOPSISDIV>', "\n"); + +## FIXME! Must be made into block elements!! +#sgml('<FUNCSYNOPSIS>', \&bold_on); +#sgml('</FUNCSYNOPSIS>', \&font_off); +#sgml('<CMDSYNOPSIS>', \&bold_on); +#sgml('</CMDSYNOPSIS>', \&font_off); + +man_sgml('<FUNCSYNOPSIS>', sub { + man_output("\n.sp\n"); + bold_on(); +}); +man_sgml('</FUNCSYNOPSIS>', sub { + font_off(); + man_output("\n"); +}); + +man_sgml('<CMDSYNOPSIS>', "\n\n"); +man_sgml('</CMDSYNOPSIS>', "\n\n"); + +man_sgml('<FUNCPROTOTYPE>', "\n.sp\n"); + +# Arguments to functions. This is C convention. +man_sgml('<PARAMDEF>', '('); +man_sgml('</PARAMDEF>', ");\n"); +man_sgml('<VOID>', "(void);\n"); + + + +sub arg_start +{ + # my $choice = $_[0]->attribute('CHOICE')->value; + + # The content model for CmdSynopsis doesn't include #PCDATA, + # so we won't see any of the whitespace in the source file, + # so we have to add it after each component. + output ' '; + + if($_[0]->attribute('CHOICE')->value =~ /opt/i) { + output '[ '; + } + bold_on(); +} +sub arg_end +{ + font_off(); + if($_[0]->attribute('REP')->value =~ /^Repeat/i) { + italic_on(); + output ' ...'; + font_off(); + } + if($_[0]->attribute('CHOICE')->value =~ /opt/i) { + output '] '; + } +} + +sgml('<ARG>', \&arg_start); +sgml('</ARG>', \&arg_end); +sgml('<GROUP>', \&arg_start); +sgml('</GROUP>', \&arg_end); + +sgml('<OPTION>', \&bold_on); +sgml('</OPTION>', \&font_off); + +# FIXME: This is one _blank_ line. +man_sgml('<SBR>', "\n\n"); + + +######################################################################## +# +# General sections +# +######################################################################## + +# The name of the section is handled by TITLE. This just sets +# up the roff markup. +man_sgml('<REFSECT1>', "\n.SH "); +man_sgml('<REFSECT2>', "\n.SS "); +man_sgml('<REFSECT3>', "\n.SS "); + + +######################################################################## +# +# Titles, metadata. +# +######################################################################## + +sgml('<TITLE>', sub { + if($_[0]->in('REFERENCE') or $_[0]->in('BOOK')) { + $write_manpages = 1; + } + save_cdata(); +}); +sgml('</TITLE>', sub { + my $title = fold_string(pop_output()); + $raw_cdata--; + + if($_[0]->in('REFERENCE') or $_[0]->in('BOOK')) { + # We use TITLE of enclosing Reference or Book as manual name + $manpage_manual = $title; + $write_manpages = 0; + } + elsif(exists $_[0]->parent->ext->{'title'}) { + # By far the easiest case. Just fold the string as + # above, and then set the parent element's variable. + $_[0]->parent->ext->{'title'} = $title; + } + else { + # If the parent element's handlers are lazy, + # output the folded string for them :) + # We assume they want uppercase and a newline. + output '"', uc($title), "\"\n"; + $newline_last = 1; + } +}); + +sgml('<ATTRIBUTION>', sub { push_output('string') }); +sgml('</ATTRIBUTION>', sub { $_[0]->parent->ext->{'attribution'} = pop_output(); }); + + +# IGNORE. +sgml('<DOCINFO>', sub { push_output('nul'); }); +sgml('</DOCINFO>', sub { pop_output(); }); +sgml('<REFSECT1INFO>', sub { push_output('nul'); }); +sgml('</REFSECT1INFO>', sub { pop_output(); }); +sgml('<REFSECT2INFO>', sub { push_output('nul'); }); +sgml('</REFSECT2INFO>', sub { pop_output(); }); +sgml('<REFSECT3INFO>', sub { push_output('nul'); }); +sgml('</REFSECT3INFO>', sub { pop_output(); }); + +sgml('<INDEXTERM>', sub { push_output('nul'); }); +sgml('</INDEXTERM>', sub { pop_output(); }); + + +######################################################################## +# +# Set bold on enclosed content +# +######################################################################## + +sgml('<APPLICATION>', \&bold_on); sgml('</APPLICATION>', \&font_off); + +sgml('<CLASSNAME>', \&bold_on); sgml('</CLASSNAME>', \&font_off); +sgml('<STRUCTNANE>', \&bold_on); sgml('</STRUCTNAME>', \&font_off); +sgml('<STRUCTFIELD>', \&bold_on); sgml('</STRUCTFIELD>', \&font_off); +sgml('<SYMBOL>', \&bold_on); sgml('</SYMBOL>', \&font_off); +sgml('<TYPE>', \&bold_on); sgml('</TYPE>', \&font_off); + +sgml('<ENVAR>', \&bold_on); sgml('</ENVAR>', \&font_off); + +sgml('<FUNCTION>', \&bold_on); sgml('</FUNCTION>', \&font_off); + +sgml('<EMPHASIS>', \&bold_on); sgml('</EMPHASIS>', \&font_off); + +sgml('<ERRORNAME>', \&bold_on); sgml('</ERRORNAME>', \&font_off); +# ERRORTYPE + +sgml('<COMMAND>', \&bold_on); sgml('</COMMAND>', \&font_off); + +sgml('<GUIBUTTON>', \&bold_on); sgml('</GUIBUTTON>', \&font_off); +sgml('<GUIICON>', \&bold_on); sgml('</GUIICON>', \&font_off); +# GUILABEL +# GUIMENU +# GUIMENUITEM +# GUISUBMENU +# MENUCHOICE +# MOUSEBUTTON + +sgml('<ACCEL>', \&bold_on); sgml('</ACCEL>', \&font_off); +sgml('<KEYCAP>', \&bold_on); sgml('</KEYCAP>', \&font_off); +sgml('<KEYSYM>', \&bold_on); sgml('</KEYSYM>', \&font_off); +# KEYCODE +# KEYCOMBO +# SHORTCUT + +sgml('<USERINPUT>', \&bold_on); sgml('</USERINPUT>', \&font_off); + +sgml('<INTERFACEDEFINITION>', \&bold_on); +sgml('</INTERFACEDEFINITION>', \&font_off); + +# May need to look at the CLASS +sgml('<SYSTEMITEM>', \&bold_on); +sgml('</SYSTEMITEM>', \&font_off); + + + + + +######################################################################## +# +# Set italic on enclosed content +# +######################################################################## + +sgml('<FIRSTTERM>', \&italic_on); sgml('</FIRSTTERM>', \&font_off); + +sgml('<FILENAME>', \&italic_on); sgml('</FILENAME>', \&font_off); +sgml('<PARAMETER>', \&italic_on); sgml('</PARAMETER>', \&font_off); +sgml('<PROPERTY>', \&italic_on); sgml('</PROPERTY>', \&font_off); + +sgml('<REPLACEABLE>', sub { + italic_on(); + if($_[0]->in('TOKEN')) { + # When tokenizing, follow more 'intuitive' convention + output "<"; + } +}); +sgml('</REPLACEABLE>', sub { + if($_[0]->in('TOKEN')) { + output ">"; + } + font_off(); +}); + +sgml('<CITETITLE>', \&italic_on); sgml('</CITETITLE>', \&font_off); +sgml('<FOREIGNPHRASE>', \&italic_on); sgml('</FOREIGNPHRASE>', \&font_off); + +sgml('<LINEANNOTATION>', \&italic_on); sgml('</LINEANNOTATION>', \&font_off); + + + + + + +######################################################################## +# +# Other 'inline' elements +# +######################################################################## + +man_sgml('<EMAIL>', '<'); +man_sgml('</EMAIL>', '>'); +man_sgml('<OPTIONAL>', '['); +man_sgml('</OPTIONAL>', ']'); + +man_sgml('</TRADEMARK>', "\\u\\s-2TM\\s+2\\d"); + +man_sgml('<COMMENT>', "[Comment: "); +man_sgml('</COMMENT>', "]"); + +man_sgml('<QUOTE>', "``"); +man_sgml('</QUOTE>', "''"); + +#man_sgml('<LITERAL>', '"'); +#man_sgml('</LITERAL>', '"'); + +# No special presentation: + +# AUTHOR +# AUTHORINITIALS + +# ABBREV +# ACTION +# ACRONYM +# ALT +# CITATION +# PHRASE +# QUOTE +# WORDASWORD + +# COMPUTEROUTPUT +# MARKUP +# PROMPT +# RETURNVALUE +# SGMLTAG +# TOKEN + +# DATABASE +# HARDWARE +# INTERFACE +# MEDIALABEL + +# There doesn't seem to be a good way to represent LITERAL in -man + + + +######################################################################## +# +# Paragraph and paragraph-like elements +# +######################################################################## + +sub para_start { + output "\n" unless $newline_last++; + + # In lists, etc., don't start paragraph with .PP since + # the indentation will be gone. + + if($_[0]->parent->ext->{'nobreak'}==1) { + # Usually this is the FIRST element of + # a hanging tag, so we MUST not do a full + # paragraph break. + $_[0]->parent->ext->{'nobreak'} = 2; + } elsif($_[0]->parent->ext->{'nobreak'}==2) { + # Usually these are the NEXT elements of + # a hanging tag. If we break using a blank + # line, we're okay. + output "\n"; + } else { + # Normal case. (For indented blocks too, at least + # -man isn't so braindead in this area.) + output ".PP\n"; + } +} +# Actually applies to a few other block elements as well +sub para_end { + output "\n" unless $newline_last++; +} + +sgml('<PARA>', \¶_start); +sgml('</PARA>', \¶_end); +sgml('<SIMPARA>', \¶_start); +sgml('</SIMPARA>', \¶_end); + +# Nothing special, except maybe FIXME set nobreak. +sgml('<INFORMALEXAMPLE>', \¶_start); +sgml('</INFORMALEXAMPLE>', \¶_end); + + + + + +######################################################################## +# +# Blocks using SS sections +# +######################################################################## + +# FIXME: We need to consider the effects of SS +# in a hanging tag :( + +# Complete with the optional-title dilemma (again). +sgml('<ABSTRACT>', sub { + $_[0]->ext->{'title'} = 'ABSTRACT'; + output "\n" unless $newline_last++; + push_output('string'); +}); +sgml('</ABSTRACT>', sub { + my $content = pop_output(); + + # As ABSTRACT is never on the same level as RefSect1, + # this leaves us with only .SS in terms of -man macros. + output ".SS \"", uc($_[0]->ext->{'title'}), "\"\n"; + + output $content; + output "\n" unless $newline_last++; +}); + +# Ah, I needed a break. Example always has a title. +man_sgml('<EXAMPLE>', "\n.SS "); +sgml('</EXAMPLE>', \¶_end); + +# Same with sidebar. +man_sgml('<SIDEBAR>', "\n.SS "); +sgml('</SIDEBAR>', \¶_end); + +# NO title. +man_sgml('<HIGHLIGHTS>', "\n.SS HIGHLIGHTS\n"); +sgml('</HIGHLIGHTS>', \¶_end); + + + + +######################################################################## +# +# Indented 'Block' elements +# +######################################################################## + +sub indent_block_start +{ + output "\n" unless $newline_last++; + output ".sp\n.RS\n"; +} +sub indent_block_end +{ + output "\n" unless $newline_last++; + output ".RE\n"; +} + +# This element is almost like an admonition (below), +# only the default title is blank :) + +sgml('<BLOCKQUOTE>', sub { + $_[0]->ext->{'title'} = ''; + output "\n" unless $newline_last++; + push_output('string'); +}); +sgml('</BLOCKQUOTE>', sub { + my $content = pop_output(); + + indent_block_start(); + + if($_[0]->ext->{'title'}) { + output ".B \"", $_[0]->ext->{'title'}, ":\"\n"; + } + + output $content; + + if($_[0]->ext->{'attribution'}) { + output "\n" unless $newline_last++; + # One place where roff's space-sensitivity makes sense :) + output "\n -- "; + output $_[0]->ext->{'attribution'} . "\n"; + } + + indent_block_end(); +}); + +# Set off admonitions from the rest of the text by indenting. +# FIXME: Need to check if this works inside paragraphs, not enclosing them. +sub admonition_end { + my $content = pop_output(); + + indent_block_start(); + + # When the admonition is only one paragraph, + # it looks nicer if the title was inline. + my $num_para; + while ($content =~ /^\.PP/gm) { $num_para++ } + if($num_para==1) { + $content =~ s/^\.PP\n//; + } + + output ".B \"" . $_[0]->ext->{'title'} . ":\"\n"; + output $content; + + indent_block_end(); +} + +sgml('<NOTE>', sub { + # We can't see right now whether or not there is a TITLE + # element, so we have to save the output now and add it back + # at the end of this admonition. + $_[0]->ext->{'title'} = 'Note'; + + # Although admonition_end's indent_block_start will do this, + # we need to synchronize the output _now_ + output "\n" unless $newline_last++; + + push_output('string'); +}); +sgml('</NOTE>', \&admonition_end); + +# Same as above. +sgml('<WARNING>', sub { + $_[0]->ext->{'title'} = 'Warning'; + output "\n" unless $newline_last++; + push_output('string'); +}); +sgml('</WARNING>', \&admonition_end); + +sgml('<TIP>', sub { + $_[0]->ext->{'title'} = 'Tip'; + output "\n" unless $newline_last++; + push_output('string'); +}); +sgml('</TIP>', \&admonition_end); +sgml('<CAUTION>', sub { + $_[0]->ext->{'title'} = 'Caution'; + output "\n" unless $newline_last++; + push_output('string'); +}); +sgml('</CAUTION>', \&admonition_end); + +sgml('<IMPORTANT>', sub { + $_[0]->ext->{'title'} = 'Important'; + output "\n" unless $newline_last++; + push_output('string'); +}); +sgml('</IMPORTANT>', \&admonition_end); + + + + + + + + + + + + +######################################################################## +# +# Verbatim displays. +# +######################################################################## + +sub verbatim_start { + output "\n" unless $newline_last++; + + if($_[0]->parent->ext->{'nobreak'}==1) { + # Usually this is the FIRST element of + # a hanging tag, so we MUST not do a full + # paragraph break. + $_[0]->parent->ext->{'nobreak'} = 2; + } else { + output "\n"; + } + + output(".nf\n") unless $nocollapse_whitespace++; +} + +sub verbatim_end { + output "\n" unless $newline_last++; + output(".fi\n") unless --$nocollapse_whitespace; +} + +sgml('<PROGRAMLISTING>', \&verbatim_start); +sgml('</PROGRAMLISTING>', \&verbatim_end); + +sgml('<SCREEN>', \&verbatim_start); +sgml('</SCREEN>', \&verbatim_end); + +sgml('<LITERALLAYOUT>', \&verbatim_start); +sgml('</LITERALLAYOUT>', \&verbatim_end); + +#sgml('<SYNOPSIS>', sub { +# if($_[0]->attribute('FORMAT')->value =~ /linespecific/i) { +# &verbatim_start; +# } else { +# roffcmd(""); +# } +#}); +# +#sgml('</SYNOPSIS>', sub { +# if($_[0]->attribute('FORMAT')->value =~ /linespecific/i) { +# &verbatim_end; +# } +# else { +# roffcmd("");# not sure about this. +# } +#}); +sgml('<SYNOPSIS>', \&verbatim_start); +sgml('</SYNOPSIS>', \&verbatim_end); + + + + + + + + + +######################################################################## +# +# Lists +# +######################################################################## + +# Indent nested lists. +sub indent_list_start { + if($list_nestlevel++) { + output "\n" unless $newline_last++; + output ".RS\n"; + } +} +sub indent_list_end { + if(--$list_nestlevel) { + output "\n" unless $newline_last++; + output ".RE\n"; + } +} + +sgml('<VARIABLELIST>', \&indent_list_start); +sgml('</VARIABLELIST>', \&indent_list_end); +sgml('<ITEMIZEDLIST>', \&indent_list_start); +sgml('</ITEMIZEDLIST>', \&indent_list_end); +sgml('<ORDEREDLIST>', sub { + indent_list_start(); + $_[0]->ext->{'count'} = 1; +}); +sgml('</ORDEREDLIST>', \&indent_list_end); + +# Output content on one line, bolded. +sgml('<TERM>', sub { + output "\n" unless $newline_last++; + output ".TP\n"; + bold_on(); + push_output('string'); +}); +sgml('</TERM>', sub { + my $term = pop_output(); + $term =~ tr/\n/ /; + output $term; + font_off(); + output "\n"; + $newline_last = 1; +}); + +sgml('<LISTITEM>', sub { + # A bulleted list. + if($_[0]->in('ITEMIZEDLIST')) { + output "\n" unless $newline_last++; + output ".TP 0.2i\n\\(bu\n"; + } + + # Need numbers. + # Assume Arabic numeration for now. + elsif($_[0]->in('ORDEREDLIST')) { + output "\n" unless $newline_last++; + output ".TP ", $_[0]->parent->ext->{'count'}++, ". \n"; + } + + $_[0]->ext->{'nobreak'} = 1; +}); + +sgml('<SIMPLELIST>', sub { + $_[0]->ext->{'first_member'} = 1; +}); + +sgml('<MEMBER>', sub { + my $parent = $_[0]->parent; + + if($parent->attribute('TYPE')->value =~ /Inline/i) { + if($parent->ext->{'first_member'}) { + # If this is the first member don't put any commas + $parent->ext->{'first_member'} = 0; + } else { + output ", "; + } + } elsif($parent->attribute('TYPE')->value =~ /Vert/i) { + output "\n" unless $newline_last++; + output "\n"; + } +}); + + + + + +######################################################################## +# +# Stuff we don't know how to handle (yet) +# +######################################################################## + +# Address blocks: + +# Credit stuff: +# ACKNO +# ADDRESS +# AFFILIATION +# ARTPAGENUMS +# ATTRIBUTION +# AUTHORBLURB +# AUTHORGROUP +# OTHERCREDIT +# HONORIFIC + +# Areas: +# AREA +# AREASET +# AREASPEC + + + + + +######################################################################## +# +# Linkage, cross references +# +######################################################################## + +# Print the URL +sgml('</ULINK>', sub { +# output ' <URL:', $_[0]->attribute('URL')->value, '>'; + $newline_last = 0; +}); + +# If cross reference target is a RefEntry, +# output CiteRefEntry-style references. +sgml('<XREF>', sub { + my $id = $_[0]->attribute('LINKEND')->value; + my $manref = $Refs->get("refentry:$id"); + + if($manref) { + my ($title, $sect) = ($manref =~ /(.*)(\(.*\))/); + bold_on(); + output $title; + font_off(); + output $sect; + } else { + $blank_xrefs++ if $write_manpages; + output "[XRef to $id]"; + } + + $newline_last = 0; +}); + +# Anchor + + + + +######################################################################## +# +# Other handlers +# +######################################################################## + +man_sgml('|[lt ]|', '<'); +man_sgml('|[gt ]|', '>'); +man_sgml('|[amp ]|', '&'); + +# +# Default handlers (uncomment these if needed). Right now, these are set +# up to gag on any unrecognised elements, sdata, processing-instructions, +# or entities. +# +# sgml('start_element',sub { die "Unknown element: " . $_[0]->name; }); +# sgml('end_element',''); + +# This is for weeding out and escaping certain characters. +# This looks like it's inefficient since it's done on every line, but +# in reality, SGMLSpm and sgmlspl parsing ESIS takes _much_ longer. + +sgml('cdata', sub +{ + if(!$write_manpages) { return; } + elsif($raw_cdata) { output $_[0]; return; } + + # Escape backslashes + $_[0] =~ s/\\/\\\\/g; + + # In non-'pre'-type elements: + if(!$nocollapse_whitespace) { + # Change tabs to spaces + $_[0] =~ tr/\t/ /; + + # Do not allow indents at beginning of line + # groff chokes on that. + if($newline_last) { + $_[0] =~ s/^ +//; + + # If the line is all blank, don't do anything. + if($_[0] eq '') { return; } + + $_[0] =~ s/^\./\\\&\./; + + # Argh... roff doesn't like ' either... + $_[0] =~ s/^\'/\\\&\'/; + } + } + + $newline_last = 0; + + output $_[0]; +}); + + +# When in whitespace-collapsing mode, we disallow consecutive newlines. + +sgml('re', sub +{ + if($nocollapse_whitespace || !$newline_last) { + output "\n"; + } + + $newline_last = 1; +}); + +sgml('sdata',sub { die "Unknown SDATA: " . $_[0]; }); +sgml('pi',sub { die "Unknown processing instruction: " . $_[0]; }); +sgml('entity',sub { die "Unknown external entity: " . $_[0]->name; }); +sgml('start_subdoc',sub { die "Unknown subdoc entity: " . $_[0]->name; }); +sgml('end_subdoc',''); +sgml('conforming',''); + +1; + diff --git a/doc/index.db b/doc/index.db new file mode 100644 index 0000000..427c2e9 --- /dev/null +++ b/doc/index.db @@ -0,0 +1,28 @@ +<!DOCTYPE reference PUBLIC "-//OASIS//DTD DocBook V3.1//EN"[ +<!ENTITY snapshot SYSTEM "snapshot.db"> +<!ENTITY ping SYSTEM "ping.sgml"> +<!ENTITY rdisc SYSTEM "rdisc.sgml"> +<!ENTITY arping SYSTEM "arping.sgml"> +<!ENTITY clockdiff SYSTEM "clockdiff.sgml"> +<!ENTITY tracepath SYSTEM "tracepath.sgml"> +<!ENTITY traceroute6 SYSTEM "traceroute6.sgml"> +<!ENTITY rarpd SYSTEM "rarpd.sgml"> +<!ENTITY ninfod SYSTEM "ninfod.sgml"> +<!ENTITY tftpd SYSTEM "tftpd.sgml"> +<!ENTITY pg3 SYSTEM "pg3.sgml"> +]> +<reference id="index"> +<title>System Manager's Manual: iputils</title> + +&ping; +&arping; +&clockdiff; +&rarpd; +&tracepath; +&traceroute6; +&tftpd; +&ninfod; +&rdisc; +&pg3; + +</reference> diff --git a/doc/index.out b/doc/index.out new file mode 100644 index 0000000..fe8ef2e --- /dev/null +++ b/doc/index.out @@ -0,0 +1,87 @@ +\BOOKMARK [1]{0.1.1}{ping}{} +\BOOKMARK [2]{0.1.1.2}{Name}{0.1.1} +\BOOKMARK [2]{0.1.2.2}{Synopsis}{0.1.1} +\BOOKMARK [2]{0.1.3.2}{DESCRIPTION}{0.1.1} +\BOOKMARK [2]{0.1.4.2}{OPTIONS}{0.1.1} +\BOOKMARK [2]{0.1.5.2}{ICMP PACKET DETAILS}{0.1.1} +\BOOKMARK [2]{0.1.6.2}{DUPLICATE AND DAMAGED PACKETS}{0.1.1} +\BOOKMARK [2]{0.1.7.2}{TRYING DIFFERENT DATA PATTERNS}{0.1.1} +\BOOKMARK [2]{0.1.8.2}{TTL DETAILS}{0.1.1} +\BOOKMARK [2]{0.1.9.2}{BUGS}{0.1.1} +\BOOKMARK [2]{0.1.10.2}{SEE ALSO}{0.1.1} +\BOOKMARK [2]{0.1.11.2}{HISTORY}{0.1.1} +\BOOKMARK [2]{0.1.12.2}{SECURITY}{0.1.1} +\BOOKMARK [2]{0.1.13.2}{AVAILABILITY}{0.1.1} +\BOOKMARK [1]{0.2.1}{arping}{} +\BOOKMARK [2]{0.2.14.2}{Name}{0.2.1} +\BOOKMARK [2]{0.2.15.2}{Synopsis}{0.2.1} +\BOOKMARK [2]{0.2.16.2}{DESCRIPTION}{0.2.1} +\BOOKMARK [2]{0.2.17.2}{OPTIONS}{0.2.1} +\BOOKMARK [2]{0.2.18.2}{SEE ALSO}{0.2.1} +\BOOKMARK [2]{0.2.19.2}{AUTHOR}{0.2.1} +\BOOKMARK [2]{0.2.20.2}{SECURITY}{0.2.1} +\BOOKMARK [2]{0.2.21.2}{AVAILABILITY}{0.2.1} +\BOOKMARK [1]{0.3.1}{clockdiff}{} +\BOOKMARK [2]{0.3.22.2}{Name}{0.3.1} +\BOOKMARK [2]{0.3.23.2}{Synopsis}{0.3.1} +\BOOKMARK [2]{0.3.24.2}{DESCRIPTION}{0.3.1} +\BOOKMARK [2]{0.3.25.2}{OPTIONS}{0.3.1} +\BOOKMARK [2]{0.3.26.2}{WARNINGS}{0.3.1} +\BOOKMARK [2]{0.3.27.2}{SEE ALSO}{0.3.1} +\BOOKMARK [2]{0.3.28.2}{REFERENCES}{0.3.1} +\BOOKMARK [2]{0.3.29.2}{AUTHOR}{0.3.1} +\BOOKMARK [2]{0.3.30.2}{SECURITY}{0.3.1} +\BOOKMARK [2]{0.3.31.2}{AVAILABILITY}{0.3.1} +\BOOKMARK [1]{0.4.1}{rarpd}{} +\BOOKMARK [2]{0.4.32.2}{Name}{0.4.1} +\BOOKMARK [2]{0.4.33.2}{Synopsis}{0.4.1} +\BOOKMARK [2]{0.4.34.2}{DESCRIPTION}{0.4.1} +\BOOKMARK [2]{0.4.35.2}{WARNING}{0.4.1} +\BOOKMARK [2]{0.4.36.2}{OPTIONS}{0.4.1} +\BOOKMARK [2]{0.4.37.2}{SEE ALSO}{0.4.1} +\BOOKMARK [2]{0.4.38.2}{AUTHOR}{0.4.1} +\BOOKMARK [2]{0.4.39.2}{SECURITY}{0.4.1} +\BOOKMARK [2]{0.4.40.2}{AVAILABILITY}{0.4.1} +\BOOKMARK [1]{0.5.1}{tracepath}{} +\BOOKMARK [2]{0.5.41.2}{Name}{0.5.1} +\BOOKMARK [2]{0.5.42.2}{Synopsis}{0.5.1} +\BOOKMARK [2]{0.5.43.2}{DESCRIPTION}{0.5.1} +\BOOKMARK [2]{0.5.44.2}{SEE ALSO}{0.5.1} +\BOOKMARK [2]{0.5.45.2}{AUTHOR}{0.5.1} +\BOOKMARK [2]{0.5.46.2}{SECURITY}{0.5.1} +\BOOKMARK [2]{0.5.47.2}{AVAILABILITY}{0.5.1} +\BOOKMARK [1]{0.6.1}{traceroute6}{} +\BOOKMARK [2]{0.6.48.2}{Name}{0.6.1} +\BOOKMARK [2]{0.6.49.2}{Synopsis}{0.6.1} +\BOOKMARK [2]{0.6.50.2}{DESCRIPTION}{0.6.1} +\BOOKMARK [2]{0.6.51.2}{SEE ALSO}{0.6.1} +\BOOKMARK [2]{0.6.52.2}{HISTORY}{0.6.1} +\BOOKMARK [2]{0.6.53.2}{SECURITY}{0.6.1} +\BOOKMARK [2]{0.6.54.2}{AVAILABILITY}{0.6.1} +\BOOKMARK [1]{0.7.1}{tftpd}{} +\BOOKMARK [2]{0.7.55.2}{Name}{0.7.1} +\BOOKMARK [2]{0.7.56.2}{Synopsis}{0.7.1} +\BOOKMARK [2]{0.7.57.2}{DESCRIPTION}{0.7.1} +\BOOKMARK [2]{0.7.58.2}{SECURITY}{0.7.1} +\BOOKMARK [2]{0.7.59.2}{SEE ALSO}{0.7.1} +\BOOKMARK [2]{0.7.60.2}{HISTORY}{0.7.1} +\BOOKMARK [2]{0.7.61.2}{AVAILABILITY}{0.7.1} +\BOOKMARK [1]{0.8.1}{rdisc}{} +\BOOKMARK [2]{0.8.62.2}{Name}{0.8.1} +\BOOKMARK [2]{0.8.63.2}{Synopsis}{0.8.1} +\BOOKMARK [2]{0.8.64.2}{DESCRIPTION}{0.8.1} +\BOOKMARK [2]{0.8.65.2}{OPTIONS}{0.8.1} +\BOOKMARK [2]{0.8.66.2}{HISTORY}{0.8.1} +\BOOKMARK [2]{0.8.67.2}{SEE ALSO}{0.8.1} +\BOOKMARK [2]{0.8.68.2}{REFERENCES}{0.8.1} +\BOOKMARK [2]{0.8.69.2}{SECURITY}{0.8.1} +\BOOKMARK [2]{0.8.70.2}{AVAILABILITY}{0.8.1} +\BOOKMARK [1]{0.9.1}{pg3}{} +\BOOKMARK [2]{0.9.71.2}{Name}{0.9.1} +\BOOKMARK [2]{0.9.72.2}{Synopsis}{0.9.1} +\BOOKMARK [2]{0.9.73.2}{DESCRIPTION}{0.9.1} +\BOOKMARK [2]{0.9.74.2}{COMMAND}{0.9.1} +\BOOKMARK [2]{0.9.75.2}{WARNING}{0.9.1} +\BOOKMARK [2]{0.9.76.2}{AUTHOR}{0.9.1} +\BOOKMARK [2]{0.9.77.2}{SECURITY}{0.9.1} +\BOOKMARK [2]{0.9.78.2}{AVAILABILITY}{0.9.1} diff --git a/doc/iputils.db b/doc/iputils.db new file mode 100644 index 0000000..f29dfad --- /dev/null +++ b/doc/iputils.db @@ -0,0 +1,209 @@ +<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook V3.1//EN"[]> +<article id="iputils"> + <artheader> + <title>iputils: documentation directory</title> + </artheader> + +<sect1> +<title>Index</title> + +<itemizedlist> + <listitem><para> + <ulink url="ping.html">ping, ping6</ulink>. + </para></listitem> + <listitem><para> + <ulink url="arping.html">arping</ulink>. + </para></listitem> + <listitem><para> + <ulink url="clockdiff.html">clockdiff</ulink>. + </para></listitem> + <listitem><para> + <ulink url="rarpd.html">rarpd</ulink>. + </para></listitem> + <listitem><para> + <ulink url="tracepath.html">tracepath, tracepath6</ulink>. + </para></listitem> + <listitem><para> + <ulink url="traceroute6.html">traceroute6</ulink>. + </para></listitem> + <listitem><para> + <ulink url="rdisc.html">rdisc</ulink>. + </para></listitem> + <listitem><para> + <ulink url="tftpd.html">tftpd</ulink>. + </para></listitem> + <listitem><para> + <ulink url="pg3.html">pg3, ipg, pgset</ulink>. + </para></listitem> +</itemizedlist> +</sect1> + +<sect1> +<title>Historical notes</title> + +<para> +This package appeared as a desperate attempt to bring some life +to state of basic networking applets: <command/ping/, <command/traceroute/ +etc. Though it was known that port of BSD <command/ping/ to Linux +was basically broken, neither maintainers of well known (and superb) +Linux net-tools package nor maintainers of Linux distributions +worried about fixing well known bugs, which were reported in linux-kernel +and linux-net mail lists for ages, were identified and nevertheless +not repaired. So, one day 1001th resuming of the subject happened +to be the last straw to break camel's back, I just parsed my hard disks +and collected a set of utilities, which shared the following properties: +</para> + +<itemizedlist> + <listitem><para> +Small + </para></listitem> + <listitem><para> +Useful despite of this + </para></listitem> + <listitem><para> +I never seen it was made right + </para></listitem> + <listitem><para> +Not quite trivial + </para></listitem> + <listitem><para> +Demonstrating some important feature of Linux + </para></listitem> + <listitem><para> +The last but not the least, I use it more or less regularly + </para></listitem> +</itemizedlist> + +<para> +This utility set was not supposed to be a reference set or something like +that. Most of them were cloned from some originals: +<informaltable> + <tgroup cols=2><tbody> + <row> + <entry>ping</entry> + <entry>cloned of an ancient NetTools-B-xx</entry> + </row> + <row> + <entry>ping6</entry> + <entry>cloned of a very old Pedro's utility set</entry> + </row> + <row> + <entry>traceroute6</entry> + <entry>cloned of NRL Sep 96 distribution</entry> + </row> + <row> + <entry>rdisc</entry> + <entry>cloned of SUN in.rdisc</entry> + </row> + <row> + <entry>clockdiff</entry> + <entry>broken out of some BSD timed</entry> + </row> + <row> + <entry>tftpd</entry> + <entry>it is clone of some ancient NetKit package</entry> + </row> + </tbody></tgroup> +</informaltable> +</para> + +<para> +Also I added some utilities written from scratch, namely +<command/tracepath/, <command/arping/ and later <command/rarpd/ +(the last one does not satisfy all the criteria, I used it two or three +times). +</para> + +<para> +Hesitated a bit I overcame temptation to add <command/traceroute/. +The variant released by LBNL to that time was mostly sane and bugs +in it were mostly not specific to Linux, but main reason was that +the latest version of LBNL <command/traceroute/ was not +<emphasis/small/, it consisted of several files, +used a wicked (and failing with Linux :-)) autoconfiguration etc. +So, instead I assembled to iputils a simplistic <command/tracepath/ utility +and IPv6 version of traceroute, and published my +<ulink url="ftp://ftp.inr.ac.ru/ip-routing/lbl-tools"> patches</ulink>. +to LBNL <command/traceroute/ separately.<footnote><para>This was mistake. +Due to this <command/traceroute/ was in a sad state until recently. +Good news, redhat-7.2 seems to add these patches to their traceroute +rpm eventually. So, I think I will refrain of suicide for awhile. +</para></footnote> +</para> + +</sect1> + +<sect1> +<title>Installation notes</title> +<para> +<userinput/make/ to compile utilities. <userinput/make html/ to prepare +html documentation, <userinput/make man/ if you prefer man pages. +Nothing fancy, provided you have DocBook package installed. +</para> + +<para> +<userinput/make install/ installs <emphasis/only/ HTML documentation +to <filename>/usr/doc/iputils</filename>. It even does not try +to install binaries and man pages. If you read historical +notes above, the reason should be evident. Most of utilities +intersect with utilities distributed in another packages, and +making such target rewriting existing installation would be a crime +from my side. The decision what variant of <command/ping/ is preferred, +how to resolve the conflicts etc. is left to you or to person who +assembled an rpm. I vote for variant from <command/iputils/ of course. +</para> + +<para> +Anyway, select utilities which you like and install them to the places +which you prefer together with their man pages. +</para> + + +<para> +It is possible that compilation will fail, if you use some +funny Linux distribution mangling header files in some unexpected ways +(expected ones are the ways of redhat of course :-)). +I validate iputils against <ulink url="http://www.asplinux.ru">asplinux</ulink> +distribution, which is inevitably followed by validity with respect +to <ulink url="http://www.redhat.com">redhat</ulink>. +If your distribution is one of widely known ones, suse or debian, +it also will compile provided snapshot is elder than month or so and +someone reported all the problems, if they took place at all. +</para> + +<para> +<emphasis> +Anyway, please, do not abuse me complaining about some compilation problems +in any distribution different of asplinux or redhat. +If you have a fix, please, send it to +<ulink url="mailto:kuznet@ms2.inr.ac.ru">me</ulink>, +I will check that it does not break distributions mentioned above +and apply it. But I am not going to undertake any investigations, +bare reports are deemed to be routed to <filename>/dev/null</filename>. +</emphasis> +</para> + +</sect1> + +<sect1><title>Availability</title> + +<para> +The collection of documents is part of <filename/iputils/ package +and the latest versions are available in source form at +<ulink url="http://www.skbuff.net/iputils/iputils-current.tar.bz2"> +http://www.skbuff.net/iputils/iputils-current.tar.bz2</ulink>. +</para> +</sect1> + + +<sect1> +<title>Copying</title> +<para> +Different files are copyrighted by different persons and organizations +and distributed under different licenses. For details look into corresponding +source files. +</para> +</sect1> + +</article> diff --git a/doc/ninfod.sgml b/doc/ninfod.sgml new file mode 100644 index 0000000..1154a75 --- /dev/null +++ b/doc/ninfod.sgml @@ -0,0 +1,120 @@ +<refentry id="ninfod"> + +<refmeta> +<refentrytitle>ninfod</refentrytitle> +<manvolnum>8</manvolnum> +<refmiscinfo>iputils-&snapshot;</refmiscinfo> +</refmeta> + +<refnamediv> +<refname>ninfod</refname> +<refpurpose>Respond to IPv6 Node Information Queries</refpurpose> +</refnamediv> + +<refsynopsisdiv> +<cmdsynopsis> +<command>ninfod</command> +<arg choice="opt"><option>-dhv</option></arg> +<arg choice="opt">-p <replaceable/pidfile/</arg> +<arg choice="opt">-u <replaceable/user/</arg> +</cmdsynopsis> +</refsynopsisdiv> + +<refsect1><title>DESCRIPTION</title> +<para> +Responds to <ulink url="http://tools.ietf.org/rfc/rfc4620.txt">IPv6 Node Information Queries (RFC4620)</ulink> from clients. +Queries can be sent by various implementations of <command/ping6/ command. +</para> +</refsect1> + +<refsect1><title>OPTIONS</title> + +<variablelist> + + <varlistentry> + <term><option/-a/</term> + <listitem><para> +Debug mode. Do not go background. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option/-h/</term> + <listitem><para> +Show help. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option/-v/</term> + <listitem><para> +Verbose mode. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>-u <replaceable/user/</option></term> + <listitem><para> +Run as another user. +<replaceable/user/ can either be username or user ID. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>-p <replaceable/pidfile/</option></term> + <listitem><para> +File for process-id storage. +<replaceable/user/ is required to be able to create the file. + </para></listitem> + </varlistentry> +</variablelist> +</refsect1> + +<refsect1><title>SEE ALSO</title> +<para> +<link linkend="ping"> +<citerefentry><refentrytitle/ping/<manvolnum/8/</citerefentry></link>. +</para> +</refsect1> + +<refsect1><title>AUTHOR</title> +<para> +<command/ninfod/ was written by USAGI/WIDE Project. +</para> +</refsect1> + +<refsect1><title>COPYING</title> +<para> +<literallayout> +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. +</literallayout> +</para> +</refsect1> + +</refentry> diff --git a/doc/pg3.sgml b/doc/pg3.sgml new file mode 100644 index 0000000..04c80fe --- /dev/null +++ b/doc/pg3.sgml @@ -0,0 +1,175 @@ +<refentry id="pg3"> + +<refmeta> +<refentrytitle>pg3</refentrytitle> +<manvolnum>8</manvolnum> +<refmiscinfo>iputils-&snapshot;</refmiscinfo> +</refmeta> + + +<refnamediv> +<refname>pg3, ipg, pgset</refname> +<refpurpose>send stream of UDP packets</refpurpose> +</refnamediv> + +<refsynopsisdiv> +<cmdsynopsis> +<command>source ipg</command> +</cmdsynopsis> +<cmdsynopsis> +<command>pg</command> +</cmdsynopsis> +<cmdsynopsis> +<command>pgset</command> +<arg choice="req"><replaceable/COMMAND/</arg> +</cmdsynopsis> +</refsynopsisdiv> + +<refsect1><title>DESCRIPTION</title> +<para> +<command/ipg/ is not a program, it is script which should be sourced +to <command/bash/. When sourced it loads module <filename/pg3/ and +exports a few of functions accessible from parent shell. These macros +are <command/pg/ to start packet injection and to get the results of run; +and <command/pgset/ to setup packet generator. +</para> + +<para> +<command/pgset/ can send the following commands to module <filename/pg3/: +</para> +</refsect1> + +<refsect1><title>COMMAND</title> + +<variablelist> + + <varlistentry> + <term><option>odev <replaceable/DEVICE/</option></term> + <listitem><para> +Name of Ethernet device to test. See +<link linkend="pg3.warning">warning</link> below. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>pkt_size <replaceable/BYTES/</option></term> + <listitem><para> +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. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>frags <replaceable/NUMBER/</option></term> + <listitem><para> +Each packet will contain <replaceable/NUMBER/ of fragments. +Maximal amount for linux-2.4 is 6. Far not all the devices support +fragmented buffers. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>count <replaceable/NUMBER/</option></term> + <listitem><para> +Send stream of <replaceable/NUMBER/ of packets and stop after this. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>ipg <replaceable/TIME/</option></term> + <listitem><para> +Introduce artificial delay between packets of <replaceable/TIME/ +microseconds. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>dst <replaceable/IP_ADDRESS/</option></term> + <listitem><para> +Select IP destination where the stream is sent to. +Beware, never set this address at random. <command/pg3/ is not a toy, +it creates really tough stream. Default value is 0.0.0.0. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>dst <replaceable/MAC_ADDRESS/</option></term> + <listitem><para> +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. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>stop</option></term> + <listitem><para> +Abort packet injection. + </para></listitem> + </varlistentry> + +</variablelist> +</refsect1> + +<refsect1 id="pg3.warning"><title>WARNING</title> +<para> +When output device is set to some random device different +of hardware Ethernet device, <command/pg3/ will crash kernel. +</para> +<para> +Do not use it on VLAN, ethertap, VTUN and other devices, +which emulate Ethernet not being real Ethernet in fact. +</para> +</refsect1> + +<refsect1><title>AUTHOR</title> +<para> +<command/pg3/ was written by <ulink url="mailto:robert.olsson@its.uu.se"> +Robert Olsson <robert.olsson@its.uu.se></ulink>. +</para> +</refsect1> + +<refsect1><title>SECURITY</title> +<para> +This can be used only by superuser. +</para> +<para> +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. +</para> +</refsect1> + +<refsect1><title>AVAILABILITY</title> +<para> +<command/pg3/ is part of <filename/iputils/ package +and the latest versions are available in source form at +<ulink url="http://www.skbuff.net/iputils/iputils-current.tar.bz2"> +http://www.skbuff.net/iputils/iputils-current.tar.bz2</ulink>. +</para> +</refsect1> + +<![IGNORE[ +<refsect1><title>COPYING</title> +<para> +<literallayout> +This documentation is free software; you can redistribute +it and/or modify it under the terms of the GNU General Public +License Version 2. + +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. + +For more details see the file COPYING in the source +distribution of Linux kernel of version 2.4. +</literallayout> +</para> +</refsect1> +]]> + +</refentry> diff --git a/doc/ping.sgml b/doc/ping.sgml new file mode 100644 index 0000000..f77276b --- /dev/null +++ b/doc/ping.sgml @@ -0,0 +1,683 @@ +<refentry id="ping"> + +<refmeta> +<refentrytitle>ping</refentrytitle> +<manvolnum>8</manvolnum> +<refmiscinfo>iputils-&snapshot;</refmiscinfo> +</refmeta> + +<refnamediv> +<refname>ping, ping6</refname> +<refpurpose>send ICMP ECHO_REQUEST to network hosts</refpurpose> +</refnamediv> + +<refsynopsisdiv> +<cmdsynopsis> +<command>ping</command> +<arg choice="opt"><option>-aAbBdDfhLnOqrRUvV</option></arg> +<arg choice="opt">-c <replaceable/count/</arg> +<arg choice="opt">-F <replaceable/flowlabel/</arg> +<arg choice="opt">-i <replaceable/interval/</arg> +<arg choice="opt">-I <replaceable/interface/</arg> +<arg choice="opt">-l <replaceable/preload/</arg> +<arg choice="opt">-m <replaceable/mark/</arg> +<arg choice="opt">-M <replaceable/pmtudisc_option/</arg> +<arg choice="opt">-N <replaceable/nodeinfo_option/</arg> +<arg choice="opt">-w <replaceable/deadline/</arg> +<arg choice="opt">-W <replaceable/timeout/</arg> +<arg choice="opt">-p <replaceable/pattern/</arg> +<arg choice="opt">-Q <replaceable/tos/</arg> +<arg choice="opt">-s <replaceable/packetsize/</arg> +<arg choice="opt">-S <replaceable/sndbuf/</arg> +<arg choice="opt">-t <replaceable/ttl/</arg> +<arg choice="opt">-T <replaceable/timestamp option/</arg> +<arg choice="opt" rep="repeat"><replaceable/hop/</arg> +<arg choice="req"><replaceable/destination/</arg> +</cmdsynopsis> +</refsynopsisdiv> + +<refsect1><title>DESCRIPTION</title> +<para> +<command/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 <structname/struct timeval/ and then an arbitrary +number of ``pad'' bytes used to fill out the packet. +</para> +<para> +<command/ping6/ is IPv6 version of <command/ping/, and can also send Node Information Queries (RFC4620). +Intermediate <replaceable/hop/s may not be allowed, because IPv6 source routing was deprecated (RFC5095). +</para> +</refsect1> + +<refsect1><title>OPTIONS</title> + +<variablelist> + <varlistentry> + <term><option/-a/</term> + <listitem><para> +Audible ping. + </para></listitem> + </varlistentry> + <varlistentry> + <term><option/-A/</term> + <listitem><para> +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. + </para></listitem> + </varlistentry> + <varlistentry> + <term><option/-b/</term> + <listitem><para> +Allow pinging a broadcast address. + </para></listitem> + </varlistentry> + <varlistentry> + <term><option/-B/</term> + <listitem><para> +Do not allow <command/ping/ to change source address of probes. +The address is bound to one selected when <command/ping/ starts. + </para></listitem> + </varlistentry> + <varlistentry> + <term><option><anchor id="ping.count">-c <replaceable/count/</option></term> + <listitem><para> +Stop after sending <replaceable/count/ ECHO_REQUEST +packets. With +<link linkend="ping.deadline"><replaceable/deadline/</link> +option, <command/ping/ waits for +<replaceable/count/ ECHO_REPLY packets, until the timeout expires. + </para></listitem> + </varlistentry> + <varlistentry> + <term><option/-d/</term> + <listitem><para> +Set the <constant/SO_DEBUG/ option on the socket being used. +Essentially, this socket option is not used by Linux kernel. + </para></listitem> + </varlistentry> + <varlistentry> + <term><option/-D/</term> + <listitem><para> +Print timestamp (unix time + microseconds as in gettimeofday) before +each line. + </para></listitem> + </varlistentry> + <varlistentry> + <term><option/-f/</term> + <listitem><para> +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. + </para></listitem> + </varlistentry> + <varlistentry> + <term><option>-F <replaceable/flow label/</option></term> + <listitem><para> +<command/ping6/ only. +Allocate and set 20 bit flow label (in hex) on echo request packets. +If value is zero, kernel allocates random flow label. + </para></listitem> + </varlistentry> + <varlistentry> + <term><option/-h/</term> + <listitem><para> +Show help. + </para></listitem> + <varlistentry> + <term><option>-i <replaceable/interval/</option></term> + <listitem><para> +Wait <replaceable/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 0.2 seconds. + </para></listitem> + </varlistentry> + <varlistentry> + <term><option>-I <replaceable/interface address/</option></term> + <listitem><para> +Set source address to specified interface address. Argument +may be numeric IP address or name of device. When pinging IPv6 +link-local address this option is required. + </para></listitem> + </varlistentry> + <varlistentry> + <term><option>-l <replaceable/preload/</option></term> + <listitem><para> +If <replaceable/preload/ is specified, +<command/ping/ sends that many packets not waiting for reply. +Only the super-user may select preload more than 3. + </para></listitem> + </varlistentry> + <varlistentry> + <term><option/-L/</term> + <listitem><para> +Suppress loopback of multicast packets. This flag only applies if the ping +destination is a multicast address. + </para></listitem> + </varlistentry> + <varlistentry> + <term><option>-m <replaceable/mark/</option></term> + <listitem><para> +use <replaceable/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. + </para></listitem> + </varlistentry> + <varlistentry> + <term><option>-M <replaceable/pmtudisc_opt/</option></term> + <listitem><para> +Select Path MTU Discovery strategy. +<replaceable/pmtudisc_option/ may be either <replaceable/do/ +(prohibit fragmentation, even local one), +<replaceable/want/ (do PMTU discovery, fragment locally when packet size +is large), or <replaceable/dont/ (do not set DF flag). + </para></listitem> + </varlistentry> + <varlistentry> + <term><option>-N <replaceable/nodeinfo_option/</option></term> + <listitem><para> +<command/ping6/ only. +Send ICMPv6 Node Information Queries (RFC4620), instead of Echo Request. + <variablelist> + <varlistentry> + <term><option>help</option></term> + <listitem><para>Show help for NI support.</para></listitem> + </varlistentry> + </variablelist> + <variablelist> + <varlistentry> + <term><option>name</option></term> + <listitem><para>Queries for Node Names.</para></listitem> + </varlistentry> + </variablelist> + <variablelist> + <varlistentry> + <term><option>ipv6</option></term> + <listitem><para>Queries for IPv6 Addresses. There are several IPv6 specific flags. + <variablelist> + <varlistentry> + <term><option>ipv6-global</option></term> + <listitem><para>Request IPv6 global-scope addresses.</para></listitem> + </varlistentry> + </variablelist> + <variablelist> + <varlistentry> + <term><option>ipv6-sitelocal</option></term> + <listitem><para>Request IPv6 site-local addresses.</para></listitem> + </varlistentry> + </variablelist> + <variablelist> + <varlistentry> + <term><option>ipv6-linklocal</option></term> + <listitem><para>Request IPv6 link-local addresses.</para></listitem> + </varlistentry> + </variablelist> + <variablelist> + <varlistentry> + <term><option>ipv6-all</option></term> + <listitem><para>Request IPv6 addresses on other interfaces.</para></listitem> + </varlistentry> + </variablelist> + </para></listitem> + </varlistentry> + </variablelist> + <variablelist> + <varlistentry> + <term><option>ipv4</option></term> + <listitem><para>Queries for IPv4 Addresses. There is one IPv4 specific flag. + <variablelist> + <varlistentry> + <term><option>ipv4-all</option></term> + <listitem><para>Request IPv4 addresses on other interfaces.</para></listitem> + </varlistentry> + </variablelist> + </para></listitem> + </varlistentry> + </variablelist> + <variablelist> + <varlistentry> + <term><option>subject-ipv6=<replaceable/ipv6addr/</option></term> + <listitem><para>IPv6 subject address.</para></listitem> + </varlistentry> + </variablelist> + <variablelist> + <varlistentry> + <term><option>subject-ipv4=<replaceable/ipv4addr/</option></term> + <listitem><para>IPv4 subject address.</para></listitem> + </varlistentry> + </variablelist> + <variablelist> + <varlistentry> + <term><option>subject-name=<replaceable/nodename/</option></term> + <listitem><para>Subject name. If it contains more than one dot, + fully-qualified domain name is assumed.</para></listitem> + </varlistentry> + </variablelist> + <variablelist> + <varlistentry> + <term><option>subject-fqdn=<replaceable/nodename/</option></term> + <listitem><para>Subject name. Fully-qualified domain name is + always assumed.</para></listitem> + </varlistentry> + </variablelist> + </para></listitem> + </varlistentry> + <varlistentry> + <term><option/-n/</term> + <listitem><para> +Numeric output only. +No attempt will be made to lookup symbolic names for host addresses. + </para></listitem> + </varlistentry> + <varlistentry> + <term><option/-O/</term> + <listitem><para> +Report outstanding ICMP ECHO reply before sending next packet. +This is useful together with the timestamp <option>-D</option> to +log output to a diagnostic file and search for missing answers. + </para></listitem> + </varlistentry> + <varlistentry> + <term><option>-p <replaceable/pattern/</option></term> + <listitem><para> +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, <option>-p ff</option> will cause the sent packet +to be filled with all ones. + </para></listitem> + </varlistentry> + <varlistentry> + <term><option/-q/</term> + <listitem><para> +Quiet output. +Nothing is displayed except the summary lines at startup time and +when finished. + </para></listitem> + </varlistentry> + <varlistentry> + <term><option>-Q <replaceable/tos/</option></term> + <listitem><para> + Set Quality of Service -related bits in ICMP datagrams. + <replaceable/tos/ can be decimal (<command/ping/ only) or hex number. + </para> + <para> + 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. + </para> + <para> + 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. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term><option/-r/</term> + <listitem><para> +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 <option/-I/ is also +used. + </para></listitem> + </varlistentry> + <varlistentry> + <term><option/-R/</term> + <listitem><para> +<command/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. + </para></listitem> + </varlistentry> + <varlistentry> + <term><option>-s <replaceable/packetsize/</option></term> + <listitem><para> +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. + </para></listitem> + </varlistentry> + <varlistentry> + <term><option>-S <replaceable/sndbuf/</option></term> + <listitem><para> +Set socket sndbuf. If not specified, it is selected to buffer +not more than one packet. + </para></listitem> + </varlistentry> + <varlistentry> + <term><option>-t <replaceable/ttl/</option></term> + <listitem><para> +<command/ping/ only. +Set the IP Time to Live. + </para></listitem> + </varlistentry> + <varlistentry> + <term><option>-T <replaceable/timestamp option/</option></term> + <listitem><para> +Set special IP timestamp options. +<replaceable/timestamp option/ may be either +<replaceable/tsonly/ (only timestamps), +<replaceable/tsandaddr/ (timestamps and addresses) or +<replaceable/tsprespec host1 [host2 [host3 [host4]]]/ +(timestamp prespecified hops). + </para></listitem> + </varlistentry> + <varlistentry> + <term><option/-U/</term> + <listitem><para> +Print full user-to-user latency (the old behaviour). Normally +<command/ping/ +prints network round trip time, which can be different +f.e. due to DNS failures. + </para></listitem> + </varlistentry> + <varlistentry> + <term><option/-v/</term> + <listitem><para> +Verbose output. + </para></listitem> + </varlistentry> + <varlistentry> + <term><option/-V/</term> + <listitem><para> +Show version and exit. + </para></listitem> + </varlistentry> + <varlistentry> + <term><option><anchor id="ping.deadline">-w <replaceable/deadline/</option></term> + <listitem><para> +Specify a timeout, in seconds, before +<command/ping/ +exits regardless of how many +packets have been sent or received. In this case +<command/ping/ +does not stop after +<link linkend="ping.count"><replaceable/count/</link> +packet are sent, it waits either for +<link linkend="ping.deadline"><replaceable/deadline/</link> +expire or until +<link linkend="ping.count"><replaceable/count/</link> +probes are answered or for some error notification from network. + </para></listitem> + </varlistentry> + <varlistentry> + <term><option>-W <replaceable/timeout/</option></term> + <listitem><para> +Time to wait for a response, in seconds. The option affects only timeout +in absence of any responses, otherwise <command/ping/ waits for two RTTs. + </para></listitem> + </varlistentry> +</variablelist> + +<para> +When using <command/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 round-trip time numbers. +When the specified number of packets have been sent (and received) or +if the program is terminated with a +<constant/SIGINT/, a brief summary is displayed. Shorter current statistics +can be obtained without termination of process with signal +<constant/SIGQUIT/. +</para> + +<para> +If <command/ping/ does not receive any reply packets at all it will +exit with code 1. If a packet +<link linkend="ping.count"><replaceable/count/</link> +and +<link linkend="ping.deadline"><replaceable/deadline/</link> +are both specified, and fewer than +<link linkend="ping.count"><replaceable/count/</link> +packets are received by the time the +<link linkend="ping.deadline"><replaceable/deadline/</link> +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. +</para> + + +<para> +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 +<command/ping/ during normal operations or from automated scripts. +</para> + +</refsect1> + + +<refsect1><title>ICMP PACKET DETAILS</title> + +<para> +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 <replaceable/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). +</para> + +<para> +If the data space is at least of size of <structname/struct timeval/ +<command/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. +</para> + +</refsect1> + +<refsect1><title>DUPLICATE AND DAMAGED PACKETS</title> + +<para> +<command/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. +</para> + +<para> +Damaged packets are obviously serious cause for alarm and often +indicate broken hardware somewhere in the +<command/ping/ packet's path (in the network or in the hosts). +</para> + +</refsect1> + +<refsect1><title>TRYING DIFFERENT DATA PATTERNS</title> + +<para> +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. +</para> + +<para> +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/-p/ option of <command/ping/. +</para> + +</refsect1> + +<refsect1><title>TTL DETAILS</title> + +<para> +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. +</para> + +<para> +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). +</para> + +<para> +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 +<citerefentry><refentrytitle/telnet/<manvolnum/1/</citerefentry> +or +<citerefentry><refentrytitle/ftp/<manvolnum/1/</citerefentry>. +</para> + +<para> +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: +</para> + +<itemizedlist> + <listitem><para> +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. + </para></listitem> + <listitem><para> +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 <emphasis/from/ +the remote system <emphasis/to/ the <command/ping/ing host. + </para></listitem> + <listitem><para> +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. + </para></listitem> +</itemizedlist> + +</refsect1> + +<refsect1><title>BUGS</title> + +<itemizedlist> + <listitem><para> +Many Hosts and Gateways ignore the RECORD_ROUTE option. + </para></listitem> + <listitem><para> +The maximum IP header length is too small for options like +RECORD_ROUTE to be completely useful. +There's not much that that can be done about this, however. + </para></listitem> + <listitem><para> +Flood pinging is not recommended in general, and flood pinging the +broadcast address should only be done under very controlled conditions. + </para></listitem> +</itemizedlist> + +</refsect1> + +<refsect1><title>SEE ALSO</title> +<para> +<citerefentry><refentrytitle/netstat/<manvolnum/1/</citerefentry>, +<citerefentry><refentrytitle/ifconfig/<manvolnum/8/</citerefentry>. +</para> +</refsect1> + +<refsect1><title>HISTORY</title> +<para> +The <command/ping/ command appeared in 4.3BSD. +</para> +<para> +The version described here is its descendant specific to Linux. +</para> +</refsect1> + +<refsect1><title>SECURITY</title> +<para> +<command/ping/ requires <constant/CAP_NET_RAW/ capability +to be executed. It may be used as set-uid root. +</para> +</refsect1> + +<refsect1><title>AVAILABILITY</title> +<para> +<command/ping/ is part of <filename/iputils/ package +and the latest versions are available in source form at +<ulink url="http://www.skbuff.net/iputils/iputils-current.tar.bz2"> +http://www.skbuff.net/iputils/iputils-current.tar.bz2</ulink>. +</para> +</refsect1> + +<![IGNORE[ +<refsect1><title>COPYING</title> +<para> +<literallayout> +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. All advertising materials mentioning features or use of this software + must display the following acknowledgement: + This product includes software developed by the University of + California, Berkeley and its contributors. +4. 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. +</literallayout> +</para> +</refsect1> +]]> + + +</refentry> + diff --git a/doc/rarpd.sgml b/doc/rarpd.sgml new file mode 100644 index 0000000..9f86ef0 --- /dev/null +++ b/doc/rarpd.sgml @@ -0,0 +1,170 @@ +<refentry id="rarpd"> + +<refmeta> +<refentrytitle>rarpd</refentrytitle> +<manvolnum>8</manvolnum> +<refmiscinfo>iputils-&snapshot;</refmiscinfo> +</refmeta> + +<refnamediv> +<refname>rarpd</refname> +<refpurpose>answer RARP REQUESTs</refpurpose> +</refnamediv> + +<refsynopsisdiv> +<cmdsynopsis> +<command>arping</command> +<arg choice="opt"><option>-aAvde</option></arg> +<arg choice="opt">-b <replaceable/bootdir/</arg> +<arg choice="opt"><replaceable/interface/</arg> +</cmdsynopsis> +</refsynopsisdiv> + +<refsect1><title>DESCRIPTION</title> +<para> +Listens +<ulink url="http://tools.ietf.org/rfc/rfc903.txt">RARP</ulink> +requests from clients. Provided MAC address of client +is found in <filename>/etc/ethers</filename> database and +obtained host name is resolvable to an IP address appropriate +for attached network, <command/rarpd/ answers to client with RARPD +reply carrying an IP address. +</para> +<para> +To allow multiple boot servers on the network <command/rarpd/ +optionally checks for presence Sun-like bootable image in TFTP directory. +It should have form <userinput/Hexadecimal_IP.ARCH/, f.e. to load +sparc 193.233.7.98 <filename/C1E90762.SUN4M/ is linked to +an image appropriate for SUM4M in directory <filename>/etc/tftpboot</filename>. +</para> +</refsect1> + +<refsect1><title>WARNING</title> +<para> +This facility is deeply obsoleted by +<ulink url="http://tools.ietf.org/rfc/rfc951.txt">BOOTP</ulink> +and later +<ulink url="http://tools.ietf.org/rfc/rfc2131.txt">DHCP</ulink> protocols. +However, some clients really still need this to boot. +</para> +</refsect1> + + +<refsect1><title>OPTIONS</title> + +<variablelist> + + <varlistentry> + <term><option/-a/</term> + <listitem><para> +Listen on all the interfaces. Currently it is an internal +option, its function is overridden with <replaceable/interface/ +argument. It should not be used. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option/-A/</term> + <listitem><para> +Listen not only RARP but also ARP messages, some rare clients +use ARP by some unknown reason. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option/-v/</term> + <listitem><para> +Be verbose. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option/-d/</term> + <listitem><para> +Debug mode. Do not go to background. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option/-e/</term> + <listitem><para> +Do not check for presence of a boot image, reply if MAC address +resolves to a valid IP address using <filename>/etc/ethers</filename> +database and DNS. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>-b <replaceable/bootdir/</option></term> + <listitem><para> +TFTP boot directory. Default is <filename>/etc/tftpboot</filename> + </para></listitem> + </varlistentry> +</variablelist> +</refsect1> + +<refsect1><title>SEE ALSO</title> +<para> +<link linkend="arping"> +<citerefentry><refentrytitle/arping/<manvolnum/8/</citerefentry></link>, +<link linkend="tftpd"> +<citerefentry><refentrytitle/tftpd/<manvolnum/8/</citerefentry></link>. +</para> +</refsect1> + +<refsect1><title>AUTHOR</title> +<para> +<command/rarpd/ was written by +<ulink url="mailto:kuznet@ms2.inr.ac.ru">Alexey Kuznetsov +<kuznet@ms2.inr.ac.ru></ulink>. +It is now maintained by +<ulink url="mailto:yoshfuji@skbuff.net">YOSHIFUJI Hideaki +<yoshfuji@skbuff.net></ulink>. +</para> +</refsect1> + +<refsect1><title>SECURITY</title> +<para> +<command/rarpd/ requires <constant/CAP_NET_RAW/ capability +to listen and send RARP and ARP packets. It also needs <constant/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. +</para> +</refsect1> + +<refsect1><title>AVAILABILITY</title> +<para> +<command/rarpd/ is part of <filename/iputils/ package +and the latest versions are available in source form at +<ulink url="http://www.skbuff.net/iputils/iputils-current.tar.bz2"> +http://www.skbuff.net/iputils/iputils-current.tar.bz2</ulink>. +</para> +</refsect1> + +<![IGNORE[ +<refsect1><title>COPYING</title> +<para> +<literallayout> +This documentation is free software; you can redistribute +it and/or modify it under the terms of the GNU General Public +License Version 2. + +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. + +For more details see the file COPYING in the source +distribution of Linux kernel of version 2.4. +</literallayout> +</para> +</refsect1> +]]> + + + + +</refentry> diff --git a/doc/rdisc.sgml b/doc/rdisc.sgml new file mode 100644 index 0000000..f991cb9 --- /dev/null +++ b/doc/rdisc.sgml @@ -0,0 +1,246 @@ +<refentry id="rdisc"> + +<refmeta> +<refentrytitle>rdisc</refentrytitle> +<manvolnum>8</manvolnum> +<refmiscinfo>iputils-&snapshot;</refmiscinfo> +</refmeta> + +<refnamediv> +<refname>rdisc</refname> +<refpurpose>network router discovery daemon</refpurpose> +</refnamediv> + +<refsynopsisdiv> +<cmdsynopsis> +<command>rdisc</command> +<arg choice="opt"><option>-abdfrstvV</option></arg> +<arg choice="opt">-p <replaceable/preference/</arg> +<arg choice="opt">-T <replaceable/max_interval/</arg> +<arg choice="opt"><replaceable/send_address/</arg> +<arg choice="opt"><replaceable/receive_address/</arg> +</cmdsynopsis> +</refsynopsisdiv> + +<refsect1><title>DESCRIPTION</title> +<para> +<command/rdisc/ implements client side of the ICMP router discover protocol. +<command/rdisc/ is invoked at boot time to populate the network +routing tables with default routes. +</para> + +<para> +<command/rdisc/ listens on the ALL_HOSTS (224.0.0.1) multicast address +(or <replaceable/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. +</para> + +<para> +Optionally, <command/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 <replaceable/send_address/ provided it is given) +when it is started. +</para> + +<para> +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 +<emphasis/advertise/ message is received from the router. +The address will also be excluded from consideration if the host receives an +<emphasis/advertise/ +message with the preference being maximally negative. +</para> + +<para> +Server side of router discovery protocol is supported by Cisco IOS +and by any more or less complete UNIX routing daemon, f.e <command/gated/. +Or, <command/rdisc/ can act as responder, if compiled with -DRDISC_SERVER. +</para> + +</refsect1> + +<refsect1><title>OPTIONS</title> + +<variablelist> + <varlistentry> + <term><option/-a/</term> + <listitem><para> +Accept all routers independently of the preference they have in their +<emphasis/advertise/ messages. +Normally <command/rdisc/ only accepts (and enters in the kernel routing +tables) the router or routers with the highest preference. + </para></listitem> + </varlistentry> + <varlistentry> + <term><option/-b/</term> + <listitem><para> +Opposite to <option/-a/, i.e. install only router with the best +preference value. It is default behaviour. + </para></listitem> + </varlistentry> + <varlistentry> + <term><option/-d/</term> + <listitem><para> +Send debugging messages to syslog. + </para></listitem> + </varlistentry> + <varlistentry> + <term><option/-f/</term> + <listitem><para> +Run <command/rdisc/ forever even if no routers are found. +Normally <command/rdisc/ gives up if it has not received any +<emphasis/advertise/ message after after soliciting three times, +in which case it exits with a non-zero exit code. +If <option/-f/ is not specified in the first form then +<option/-s/ must be specified. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option/-r/</term> + <listitem><para> +Responder mode, available only if compiled with -DRDISC_SERVER. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option/-s/</term> + <listitem><para> +Send three <emphasis/solicitation/ messages initially to quickly discover +the routers when the system is booted. +When <option/-s/ is specified <command/rdisc/ +exits with a non-zero exit code if it can not find any routers. +This can be overridden with the <option/-f/ option. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>-p <replaceable/preference/</option></term> + <listitem><para> +Set preference in advertisement. +Available only with -r option. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>-T <replaceable/max_interval/</option></term> + <listitem><para> +Set maximum advertisement interval in seconds. Default is 600 secs. +Available only with -r option. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option/-t/</term> + <listitem><para> +Test mode. Do not go to background. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option/-v/</term> + <listitem><para> +Be verbose i.e. send lots of debugging messages to syslog. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option/-V/</term> + <listitem><para> +Print version and exit. + </para></listitem> + </varlistentry> +</variablelist> +</refsect1> + +<refsect1><title>HISTORY</title> +<para> +This program was developed by Sun Microsystems (see copyright +notice in source file). It was ported to Linux by +<ulink url="mailto:kuznet@ms2.inr.ac.ru">Alexey Kuznetsov +<kuznet@ms2.inr.ac.ru></ulink>. +It is now maintained by +<ulink url="mailto:yoshfuji@skbuff.net">YOSHIFUJI Hideaki +<yoshfuji@skbuff.net></ulink>. +</para> +</refsect1> + + +<refsect1><title>SEE ALSO</title> +<para> +<citerefentry><refentrytitle/icmp/<manvolnum/7/</citerefentry>, +<citerefentry><refentrytitle/inet/<manvolnum/7/</citerefentry>, +<link linkend="ping"> +<citerefentry><refentrytitle/ping/<manvolnum/8/</citerefentry></link>. +</para> +</refsect1> + +<refsect1><title>REFERENCES</title> +<para> +Deering, S.E.,ed "ICMP Router Discovery Messages", +<ulink url="http://tools.ietf.org/rfc/rfc1256.txt"> +RFC1256</ulink>, Network Information Center, SRI International, +Menlo Park, Calif., September 1991. +</para> +</refsect1> + +<refsect1><title>SECURITY</title> +<para> +<command/rdisc/ requires <constant/CAP_NET_RAW/ to listen +and send ICMP messages and capability <constant/CAP_NET_ADMIN/ +to update routing tables. +</para> +</refsect1> + +<refsect1><title>AVAILABILITY</title> +<para> +<command/rdisc/ is part of <filename/iputils/ package +and the latest versions are available in source form at +<ulink url="http://www.skbuff.net/iputils/iputils-current.tar.bz2"> +http://www.skbuff.net/iputils/iputils-current.tar.bz2</ulink>. +</para> +</refsect1> + +<![IGNORE[ +<refsect1><title>COPYING</title> +<para> +<literallayout> +Rdisc (this program) was developed by Sun Microsystems, Inc. and is +provided for unrestricted use provided that this legend is included on +all tape media and as a part of the software program in whole or part. +Users may copy or modify Rdisc without charge, and they may freely +distribute it. + +RDISC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE +WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR +PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + +Rdisc is provided with no support and without any obligation on the +part of Sun Microsystems, Inc. to assist in its use, correction, +modification or enhancement. + +SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE +INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY RDISC +OR ANY PART THEREOF. + +In no event will Sun Microsystems, Inc. be liable for any lost revenue +or profits or other special, indirect and consequential damages, even if +Sun has been advised of the possibility of such damages. + +Sun Microsystems, Inc. +2550 Garcia Avenue +Mountain View, California 94043 +</literallayout> +</para> +</refsect1> +]]> + + +</refentry> diff --git a/doc/snapshot.db b/doc/snapshot.db new file mode 100644 index 0000000..a40d174 --- /dev/null +++ b/doc/snapshot.db @@ -0,0 +1 @@ +121126 diff --git a/doc/tftpd.sgml b/doc/tftpd.sgml new file mode 100644 index 0000000..fe7fd7c --- /dev/null +++ b/doc/tftpd.sgml @@ -0,0 +1,151 @@ +<refentry id="tftpd"> + +<refmeta> +<refentrytitle>tftpd</refentrytitle> +<manvolnum>8</manvolnum> +<refmiscinfo>iputils-&snapshot;</refmiscinfo> +</refmeta> + +<refnamediv> +<refname>tftpd</refname> +<refpurpose>Trivial File Transfer Protocol server</refpurpose> +</refnamediv> + +<refsynopsisdiv> +<cmdsynopsis> +<command>tftpd</command> +<arg choice="req"><replaceable/directory/</arg> +</cmdsynopsis> +</refsynopsisdiv> + +<refsect1><title>DESCRIPTION</title> +<para> +<command/tftpd/ is a server which supports the DARPA +Trivial File Transfer Protocol +(<ulink url="http://tools.ietf.org/rfc/rfc1350.txt">RFC1350</ulink>). +The TFTP server is started +by <citerefentry><refentrytitle/inetd/<manvolnum/8/</citerefentry>. +</para> + +<para> +<replaceable/directory/ is required argument; if it is not given +<command/tftpd/ aborts. This path is prepended to any file name requested +via TFTP protocol, effectively chrooting <command/tftpd/ to this directory. +File names are validated not to escape out of this directory, however +administrator may configure such escape using symbolic links. +</para> + +<para> +It is in difference of variants of <command/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. +</para> + +<para> +In the case when <command/tftpd/ is used together with +<link linkend="rarpd"> +<citerefentry><refentrytitle/rarpd/<manvolnum/8/</citerefentry></link>, +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 +<link linkend="rarpd"> +<citerefentry><refentrytitle/rarpd/<manvolnum/8/</citerefentry></link> +for more details. +</para> +</refsect1> + +<refsect1><title>SECURITY</title> +<para> +TFTP protocol does not provide any authentication. +Due to this capital flaw <command/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. +</para> + +<para> +Impact is evident, directory exported via TFTP <emphasis/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 +<emphasis/unencrypted/ passwords and may contain some information +about the network, which you were not going to make public. +</para> + +<para> +The <command/tftpd/ server should be executed by <command/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, <command/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. +</para> + +</refsect1> + + +<refsect1><title>SEE ALSO</title> +<para> +<link linkend="rarpd"> +<citerefentry><refentrytitle/rarpd/<manvolnum/8/</citerefentry></link>, +<citerefentry><refentrytitle/tftp/<manvolnum/1/</citerefentry>, +<citerefentry><refentrytitle/inetd/<manvolnum/8/</citerefentry>. +</para> +</refsect1> + +<refsect1><title>HISTORY</title> +<para> +The <command/tftpd/ command appeared in 4.2BSD. The source in iputils +is cleaned up both syntactically (ANSIized) and semantically (UDP socket IO). +</para> +<para> +It is distributed with iputils mostly as good demo of an interesting feature +(<constant/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. +</para> +</refsect1> + + +<refsect1><title>AVAILABILITY</title> +<para> +<command/tftpd/ is part of <filename/iputils/ package +and the latest versions are available in source form at +<ulink url="http://www.skbuff.net/iputils/iputils-current.tar.bz2"> +http://www.skbuff.net/iputils/iputils-current.tar.bz2</ulink>. +</para> +</refsect1> + + +<![IGNORE[ +<refsect1><title>COPYING</title> +<para> +<literallayout> +This documentation is free software; you can redistribute +it and/or modify it under the terms of the GNU General Public +License Version 2. + +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. + +For more details see the file COPYING in the source +distribution of Linux kernel of version 2.4. +</literallayout> +</literallayout> +</para> +</refsect1> +]]> + + + +</refentry> diff --git a/doc/tracepath.sgml b/doc/tracepath.sgml new file mode 100644 index 0000000..8da7cc0 --- /dev/null +++ b/doc/tracepath.sgml @@ -0,0 +1,191 @@ +<refentry id="tracepath"> + +<refmeta> +<refentrytitle>tracepath</refentrytitle> +<manvolnum>8</manvolnum> +<refmiscinfo>iputils-&snapshot;</refmiscinfo> +</refmeta> + +<refnamediv> +<refname>tracepath, tracepath6</refname> +<refpurpose> +traces path to a network host discovering MTU along this path</refpurpose> +</refnamediv> + +<refsynopsisdiv> +<cmdsynopsis> +<command>tracepath</command> +<arg choice="opt">-n</arg> +<arg choice="opt">-b</arg> +<arg choice="opt">-l <replaceable/pktlen/</arg> +<arg choice="opt">-p <replaceable/port/</arg> +<arg choice="req"><replaceable/destination/</arg> +</cmdsynopsis> +</refsynopsisdiv> + +<refsect1><title>DESCRIPTION</title> +<para> +It traces path to <replaceable/destination/ discovering MTU along this path. +It uses UDP port <replaceable/port/ or some random port. +It is similar to <command/traceroute/, only does not require superuser +privileges and has no fancy options. +</para> + +<para> +<command/tracepath6/ is good replacement for <command/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. +</para> +</refsect1> + +<refsect1><title>OPTIONS</title> +<variablelist> + + <varlistentry> + <term><option/-n/</term> + <listitem><para> +Print primarily IP addresses numerically. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option/-b/</term> + <listitem><para> +Print both of host names and IP addresses. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option/-l/</term> + <listitem><para> +Sets the initial packet length to <replaceable/pktlen/ instead of +65536 for <command/tracepath/ or 128000 for <command/tracepath6/. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option/-p/</term> + <listitem><para> +Sets the initial destination port to use. + </para></listitem> +</variablelist> +</refsect1> + +<refsect1><title>OUTPUT</title> +<para> +<literallayout> +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 +</literallayout> +</para> + +<para> +The first column shows <literal/TTL/ of the probe, followed by colon. +Usually value of <literal/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 ?. +</para> + +<para> +The second column shows the network hop, which replied to the probe. +It is either address of router or word <literal/[LOCALHOST]/, if +the probe was not sent to the network. +</para> + +<para> +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 <literal/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. +</para> + +<para> +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. +</para> + +</refsect1> + + + + +<refsect1><title>SEE ALSO</title> +<para> +<citerefentry><refentrytitle/traceroute/<manvolnum/8/</citerefentry>, +<link linkend="traceroute6"> +<citerefentry><refentrytitle/traceroute6/<manvolnum/8/</citerefentry></link>, +<link linkend="ping"> +<citerefentry><refentrytitle/ping/<manvolnum/8/</citerefentry></link>. +</para> +</refsect1> + +<refsect1><title>AUTHOR</title> +<para> +<command/tracepath/ was written by +<ulink url="mailto:kuznet@ms2.inr.ac.ru">Alexey Kuznetsov +<kuznet@ms2.inr.ac.ru></ulink>. +</para> +</refsect1> + +<refsect1><title>SECURITY</title> +<para> +No security issues. +</para> +<para> +This lapidary deserves to be elaborated. +<command/tracepath/ is not a privileged program, unlike +<command/traceroute/, <command/ping/ and other beasts of this kind. +<command/tracepath/ may be executed by everyone who has some access +to network, enough to send UDP datagrams to investigated destination +using given port. +</para> +</refsect1> + +<refsect1><title>AVAILABILITY</title> +<para> +<command/tracepath/ is part of <filename/iputils/ package +and the latest versions are available in source form at +<ulink url="http://www.skbuff.net/iputils/iputils-current.tar.bz2"> +http://www.skbuff.net/iputils/iputils-current.tar.bz2</ulink>. +</para> +</refsect1> + +<![IGNORE[ +<refsect1><title>COPYING</title> +<para> +<literallayout> +This documentation is free software; you can redistribute +it and/or modify it under the terms of the GNU General Public +License Version 2. + +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. + +For more details see the file COPYING in the source +distribution of Linux kernel of version 2.4. +</literallayout> +</para> +</refsect1> +]]> + + + + +</refentry> diff --git a/doc/traceroute6.sgml b/doc/traceroute6.sgml new file mode 100644 index 0000000..e47dd13 --- /dev/null +++ b/doc/traceroute6.sgml @@ -0,0 +1,97 @@ +<refentry id="traceroute6"> + +<refmeta> +<refentrytitle>traceroute6</refentrytitle> +<manvolnum>8</manvolnum> +<refmiscinfo>iputils-&snapshot;</refmiscinfo> +</refmeta> + +<refnamediv> +<refname>traceroute6</refname> +<refpurpose>traces path to a network host</refpurpose> +</refnamediv> + +<refsynopsisdiv> +<cmdsynopsis> +<command>traceroute6</command> +<arg choice="opt"><option>-dnrvV</option></arg> +<arg choice="opt">-i <replaceable/interface/</arg> +<arg choice="opt">-m <replaceable/max_ttl/</arg> +<arg choice="opt">-p <replaceable/port/</arg> +<arg choice="opt">-q <replaceable/max_probes/</arg> +<arg choice="opt">-s <replaceable/source/</arg> +<arg choice="opt">-w <replaceable/wait time/</arg> +<arg choice="req"><replaceable/destination/</arg> +<arg choice="opt"><replaceable/size/</arg> +</cmdsynopsis> +</refsynopsisdiv> + +<refsect1><title>DESCRIPTION</title> +<para> +Description can be found in +<citerefentry><refentrytitle/traceroute/<manvolnum/8/</citerefentry>, +all the references to IP replaced to IPv6. It is needless to copy +the description from there. +</para> +</refsect1> + +<refsect1><title>SEE ALSO</title> +<para> +<citerefentry><refentrytitle/traceroute/<manvolnum/8/</citerefentry>, +<citerefentry><refentrytitle/tracepath/<manvolnum/8/</citerefentry>, +<citerefentry><refentrytitle/ping/<manvolnum/8/</citerefentry>. +</para> +</refsect1> + +<refsect1><title>HISTORY</title> +<para> +This program has long history. Author of <command/traceroute/ +is Van Jacobson and it first appeared in 1988. This clone is +based on a port of <command/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 +<ulink url="mailto:kuznet@ms2.inr.ac.ru">Alexey Kuznetsov +<kuznet@ms2.inr.ac.ru></ulink>. And eventually entered +<command/iputils/ package. +</para> +</refsect1> + +<refsect1><title>SECURITY</title> +<para> +<command/tracepath6/ requires <constant/CAP_NET_RAW/ capability +to be executed. It is safe to be used as set-uid root. +</para> +</refsect1> + +<refsect1><title>AVAILABILITY</title> +<para> +<command/traceroute6/ is part of <filename/iputils/ package +and the latest versions are available in source form at +<ulink url="http://www.skbuff.net/iputils/iputils-current.tar.bz2"> +http://www.skbuff.net/iputils/iputils-current.tar.bz2</ulink>. +</para> +</refsect1> + +<![IGNORE[ +<refsect1><title>COPYING</title> +<para> +<literallayout> +This documentation is free software; you can redistribute +it and/or modify it under the terms of the GNU General Public +License Version 2. + +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. + +For more details see the file COPYING in the source +distribution of Linux kernel of version 2.4. +</literallayout> +</para> +</refsect1> +]]> + + + +</refentry> 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 @@ -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.spec b/iputils.spec new file mode 100644 index 0000000..9711566 --- /dev/null +++ b/iputils.spec @@ -0,0 +1,80 @@ +# +# This spec file is for _testing_. You may use it (I do), but no warranty. +# + +%define ssdate 020124 +Summary: The ping program for checking to see if network hosts are alive. +Name: iputils +Version: 20%{ssdate} +Release: 1local +License: BSD +Group: System Environment/Daemons +Source0: iputils-ss%{ssdate}.tar.bz2 +Prefix: %{_prefix} +BuildRoot: %{_tmppath}/%{name}-root +BuildPrereq: docbook-dtd31-sgml, perl +Requires: kernel >= 2.4.7 + +%description +The iputils package contains ping, a basic networking tool. The ping +command sends a series of ICMP protocol ECHO_REQUEST packets to a +specified network host and can tell you if that machine is alive and +receiving network traffic. + +%prep +%setup -q -n %{name} + +%build +make CC=gcc3 +make man +make html + +%install +rm -rf ${RPM_BUILD_ROOT} + +mkdir -p ${RPM_BUILD_ROOT}%{_sbindir} +mkdir -p ${RPM_BUILD_ROOT}/{bin,sbin} +install -c clockdiff ${RPM_BUILD_ROOT}%{_sbindir}/ +%ifos linux +install -c arping ${RPM_BUILD_ROOT}/sbin/ +ln -s ../../sbin/arping ${RPM_BUILD_ROOT}%{_sbindir}/arping +install -c ping ${RPM_BUILD_ROOT}/bin/ +%else +install -c arping ${RPM_BUILD_ROOT}%{_sbindir}/ +install -c ping ${RPM_BUILD_ROOT}%{_sbindir}/ +%endif +install -c ping6 ${RPM_BUILD_ROOT}%{_sbindir}/ +install -c rdisc ${RPM_BUILD_ROOT}%{_sbindir}/ +install -c tracepath ${RPM_BUILD_ROOT}%{_sbindir}/ +install -c tracepath6 ${RPM_BUILD_ROOT}%{_sbindir}/ +install -c traceroute6 ${RPM_BUILD_ROOT}%{_sbindir}/ + +mkdir -p ${RPM_BUILD_ROOT}%{_mandir}/man8 +install -c doc/*.8 ${RPM_BUILD_ROOT}%{_mandir}/man8/ + +%clean +rm -rf ${RPM_BUILD_ROOT} + +%files +%defattr(-,root,root) +%doc RELNOTES doc/*.html +%{_sbindir}/clockdiff +%ifos linux +%attr(4755,root,root) /bin/ping +/sbin/arping +%{_sbindir}/arping +%else +%{_sbindir}/arping +%attr(4755,root,root) %{_sbindir}/ping +%endif +%attr(4755,root,root) %{_sbindir}/ping6 +%{_sbindir}/tracepath +%{_sbindir}/tracepath6 +%attr(4755,root,root) %{_sbindir}/traceroute6 +%{_sbindir}/rdisc +%{_mandir}/man8/* + + +%changelog +* Sat Feb 23 2001 Alexey Kuznetsov <kuznet@ms2.inr.ac.ru> + Taken iputils rpm from ASPLinux-7.2 as pattern. 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..10ff9a2 --- /dev/null +++ b/ninfod/config.h.in @@ -0,0 +1,136 @@ +/* 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 <arpa/inet.h> header file. */ +#undef HAVE_ARPA_INET_H + +/* Define to 1 if you have the <inttypes.h> 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 <limits.h> header file. */ +#undef HAVE_LIMITS_H + +/* Define to 1 if you have the <linux/rtnetlink.h> header file. */ +#undef HAVE_LINUX_RTNETLINK_H + +/* Define to 1 if you have the <memory.h> 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 <netdb.h> header file. */ +#undef HAVE_NETDB_H + +/* Define to 1 if you have the <netinet/icmp6.h> header file. */ +#undef HAVE_NETINET_ICMP6_H + +/* Define to 1 if you have the <netinet/in.h> header file. */ +#undef HAVE_NETINET_IN_H + +/* Define to 1 if you have the <netinet/ip6.h> header file. */ +#undef HAVE_NETINET_IP6_H + +/* Define to 1 if you have the <openssl/md5.h> header file. */ +#undef HAVE_OPENSSL_MD5_H + +/* Define to 1 if you have the <pthread.h> header file. */ +#undef HAVE_PTHREAD_H + +/* Define to 1 if you have the <pwd.h> header file. */ +#undef HAVE_PWD_H + +/* Define to 1 if you have the <stdint.h> header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the <stdlib.h> header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the <strings.h> header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the <string.h> 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 <syslog.h> header file. */ +#undef HAVE_SYSLOG_H + +/* Define to 1 if you have the <sys/capability.h> header file. */ +#undef HAVE_SYS_CAPABILITY_H + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the <sys/types.h> header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the <sys/uio.h> header file. */ +#undef HAVE_SYS_UIO_H + +/* Define to 1 if you have the <sys/utsname.h> header file. */ +#undef HAVE_SYS_UTSNAME_H + +/* Define to 1 if you have the <unistd.h> 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 <sys/time.h> and <time.h>. */ +#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 <sys/types.h> does not define. */ +#undef size_t diff --git a/ninfod/configure b/ninfod/configure new file mode 100755 index 0000000..deba361 --- /dev/null +++ b/ninfod/configure @@ -0,0 +1,5422 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.68. +# +# +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, +# 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 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 + +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" + 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 : + # 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 + export CONFIG_SHELL + 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+"$@"} +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_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; } + + # 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 -p'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -p' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -p' + fi +else + as_ln_s='cp -p' +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 + +if test -x / >/dev/null 2>&1; then + as_test_x='test -x' +else + if ls -dL / >/dev/null 2>&1; then + as_ls_L_option=L + else + as_ls_L_option= + fi + as_test_x=' + eval sh -c '\'' + if test -d "$1"; then + test -d "$1/."; + else + case $1 in #( + -*)set "./$1";; + esac; + case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #(( + ???[sx]*):;;*)false;;esac;fi + '\'' sh + ' +fi +as_executable_p=$as_test_x + +# 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 </dev/null +exec 6>&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 <stdio.h> +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif +#ifdef HAVE_SYS_STAT_H +# include <sys/stat.h> +#endif +#ifdef STDC_HEADERS +# include <stdlib.h> +# include <stddef.h> +#else +# ifdef HAVE_STDLIB_H +# include <stdlib.h> +# endif +#endif +#ifdef HAVE_STRING_H +# if !defined STDC_HEADERS && defined HAVE_MEMORY_H +# include <memory.h> +# endif +# include <string.h> +#endif +#ifdef HAVE_STRINGS_H +# include <strings.h> +#endif +#ifdef HAVE_INTTYPES_H +# include <inttypes.h> +#endif +#ifdef HAVE_STDINT_H +# include <stdint.h> +#endif +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#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 + $as_echo "$as_me: WARNING: if you wanted to set the --build type, don't use --host. + If a cross compiler is detected then cross compile mode will be used" >&2 + 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<lib dir> if you have libraries in a + nonstandard directory <lib dir> + LIBS libraries to pass to the linker, e.g. -l<library> + CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if + you have headers in a nonstandard directory <include dir> + 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.68 + +Copyright (C) 2010 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 || + $as_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 <limits.h> declares $2. + For example, HP-UX 11i <limits.h> declares gettimeofday. */ +#define $2 innocuous_$2 + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $2 (); below. + Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + <limits.h> exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#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.68. 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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$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 <stdio.h> +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 <stdarg.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +/* 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 { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$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 <limits.h> to <assert.h> if __STDC__ is defined, since + # <limits.h> 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 <limits.h> +#else +# include <assert.h> +#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 <ac_nonexistent.h> +_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 <limits.h> to <assert.h> if __STDC__ is defined, since + # <limits.h> 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 <limits.h> +#else +# include <assert.h> +#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 <ac_nonexistent.h> +_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" + { test -f "$ac_path_GREP" && $as_test_x "$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" + { test -f "$ac_path_EGREP" && $as_test_x "$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 <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <float.h> + +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 <string.h> + +_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 <stdlib.h> + +_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 <ctype.h> +#include <stdlib.h> +#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 <sys/types.h> +#include <sys/time.h> +#include <time.h> + +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 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 <sys/types.h> +#endif +#if HAVE_NETINET_IN_H +# include <netinet/in.h> +#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 <asm/types.h> +#include <sys/socket.h> + +" +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 <sys/types.h> + #include <sys/param.h> + +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 <sys/types.h> + #include <sys/param.h> + +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 <limits.h> defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris). + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <limits.h> + +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 <limits.h> + +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 () +{ +/* FIXME: Include the comments suggested by Paul. */ +#ifndef __cplusplus + /* Ultrix mips cc rejects this. */ + typedef int charset[2]; + const charset cs; + /* 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. */ + char *t; + 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 saying + "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ + struct s { int j; const int *ap[3]; }; + struct s *b; 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 <sys/types.h> +#include <netinet/in.h> +#include <netinet/icmp6.h> + +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 + + +LIBS="-lcrypto $LIBS" + + +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 -p'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -p' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -p' + fi +else + as_ln_s='cp -p' +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 + +if test -x / >/dev/null 2>&1; then + as_test_x='test -x' +else + if ls -dL / >/dev/null 2>&1; then + as_ls_L_option=L + else + as_ls_L_option= + fi + as_test_x=' + eval sh -c '\'' + if test -d "$1"; then + test -d "$1/."; + else + case $1 in #( + -*)set "./$1";; + esac; + case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #(( + ???[sx]*):;;*)false;;esac;fi + '\'' sh + ' +fi +as_executable_p=$as_test_x + +# 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.68. 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.68, + with options \\"\$ac_cs_config\\" + +Copyright (C) 2010 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 2>/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 +' <conf$$subs.awk | sed ' +/^[^""]/{ + N + s/\n// +} +' >>$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 +' <confdefs.h | sed ' +s/'"$ac_delim"'/"\\\ +"/g' >>$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..99f8da0 --- /dev/null +++ b/ninfod/configure.in @@ -0,0 +1,137 @@ +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(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 <sys/types.h> +#endif +#if HAVE_NETINET_IN_H +# include <netinet/in.h> +#endif +]) +AC_CHECK_HEADERS(linux/rtnetlink.h,,,[ +#include <asm/types.h> +#include <sys/socket.h> +]) +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 <sys/types.h> +#include <netinet/in.h> +#include <netinet/icmp6.h> +],[ +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) + +dnl AC_CHECK_LIB(crypto, MD5_Init, +dnl AC_DEFINE(HAVE_MD5_INIT) +dnl LIBS="-lcrypto $LIBS", +dnl ) +dnl AC_CHECK_LIB(crypto, MD5Init, +dnl AC_DEFINE(HAVE_MD5INIT) +dnl LIBS="-lcrypto $LIBS", +dnl) +LIBS="-lcrypto $LIBS" + +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/ni_ifaddrs.c b/ninfod/ni_ifaddrs.c new file mode 100644 index 0000000..4225a5a --- /dev/null +++ b/ninfod/ni_ifaddrs.c @@ -0,0 +1,541 @@ +/* $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 <string.h> +#include <time.h> +#include <malloc.h> +#include <errno.h> +#include <unistd.h> + +#include <sys/socket.h> +#include <asm/types.h> +#include <linux/netlink.h> +#include <linux/rtnetlink.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netpacket/packet.h> +#include <net/ethernet.h> /* the L2 protocols */ +#include <sys/uio.h> +#include <net/if.h> +#include <net/if_arp.h> +#include "ni_ifaddrs.h" +#include <netinet/in.h> + +#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) { + 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); + 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 result; + 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; + uint16_t *ifflist = 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); + result = 0; + break; + } + if (data == NULL) { + free_data(data); + result = -1; + break; + } + ifl = NULL; + data += NLMSG_ALIGN(sizeof(struct ni_ifaddrs)) * icnt; + xdata = data + dlen; + ifflist = xdata + xlen; + } + + 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_scope = 0, 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_scope = ifam->ifa_scope; + 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..51f6e2a --- /dev/null +++ b/ninfod/ninfod.c @@ -0,0 +1,729 @@ +/* $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 <yoshfuji@linux-ipv6.org> + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#if HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif +#if STDC_HEADERS +# include <stdio.h> +# include <stdlib.h> +# include <stddef.h> +# include <stdarg.h> +#else +# if HAVE_STDLIB_H +# include <stdlib.h> +# endif +#endif + +#if HAVE_STRING_H +# if !STDC_HEADERS && HAVE_MEMORY_H +# include <memory.h> +# endif +# include <string.h> +#endif +#if HAVE_STRINGS_H +# include <strings.h> +#endif +#if HAVE_INTTYPES_H +# include <inttypes.h> +#else +# if HAVE_STDINT_H +# include <stdint.h> +# endif +#endif +#if HAVE_LIMITS_H +# include <limits.h> +#endif +#if HAVE_UNISTD_H +# include <unistd.h> +#endif + +#if HAVE_SYS_UIO_H +#include <sys/uio.h> +#endif + +#include <sys/socket.h> + +#if HAVE_NETINET_IN_H +# include <netinet/in.h> +#endif + +#if HAVE_NETINET_ICMP6_H +# include <netinet/icmp6.h> +#endif +#ifndef HAVE_STRUCT_ICMP6_NODEINFO +# include "icmp6_nodeinfo.h" +#endif + +#if HAVE_NETDB_H +# include <netdb.h> +#endif +#include <errno.h> + +#include <signal.h> + +#if HAVE_SYSLOG_H +# include <syslog.h> +#endif + +#if HAVE_PWD_H +# include <pwd.h> +#endif + +#if HAVE_SYS_CAPABILITY_H +# include <sys/prctl.h> +# include <sys/capability.h> +#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 */ +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; + int i; + +#if NEED_IPV6CHECKSUM + 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) +{ + int err; + + DEBUG(LOG_INFO, "singnal(%d) received, quit.\n", sig); + err = unlink(opt_p); + if (err < 0) { + DEBUG(LOG_ERR, "failed to unlink file '%s' : %s\n", + opt_p, strerror(errno)); + exit(1); + } + /* closelog() */ + + exit(0); +} + +static void do_daemonize(void) +{ + FILE *fp = NULL; + struct sigaction act; + sigset_t smask; + 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, pid); + } else { + DEBUG(LOG_ERR, "pid file '%s' exists : %d\n", + opt_p, pid); + } + fclose(fp); + exit(1); + } + } + + 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); + + fp = fopen(opt_p, "w+"); + 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); + } +#if ENABLE_DEBUG + openlog(NINFOD, 0, LOG_USER); +#endif + daemonized = 1; + + if (fp) { + fprintf(fp, "%d\n", getpid()); + fclose(fp); + } +} + +/* --------- */ +#ifdef HAVE_LIBCAP +static const cap_value_t caps[] = { CAP_NET_RAW, CAP_SETUID }; +#else +static uid_t euid; +#endif + +static void limit_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); + } + + /* net_raw + setuid / net_raw */ + if (cap_set_flag(cap_p, CAP_PERMITTED, 2, caps, CAP_SET) < 0 || + cap_set_flag(cap_p, CAP_EFFECTIVE, 1, caps, CAP_SET) < 0) { + DEBUG(LOG_ERR, "cap_set_flag: %s\n", strerror(errno)); + exit(-1); + } + + 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); + } + + if (cap_free(cap_p) < 0) { + DEBUG(LOG_ERR, "cap_free: %s\n", strerror(errno)); + exit(-1); + } +#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_set_flag(cap_p, CAP_PERMITTED, 1, caps + 1, CAP_SET) < 0 || + cap_set_flag(cap_p, CAP_EFFECTIVE, 1, caps + 1, CAP_SET) < 0) { + DEBUG(LOG_ERR, "cap_set_flag: %s\n", strerror(errno)); + exit(-1); + } + + 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); + } + + if (cap_clear(cap_p) < 0) { + DEBUG(LOG_ERR, "cap_clear: %s\n", strerror(errno)); + exit(-1); + } + + if (cap_set_proc(cap_p) < 0) { + DEBUG(LOG_ERR, "cap_set_proc: %s\n", strerror(errno)); + exit(-1); + } + + if (cap_free(cap_p) < 0) { + DEBUG(LOG_ERR, "cap_free: %s\n", strerror(errno)); + exit(-1); + } + +#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; + int c; + + appname = argv[0]; + + 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); + } + + setbuf(stderr, NULL); + + if (!opt_d) + do_daemonize(); + + /* initialize */ + if (init_sock(sock) < 0) + exit(1); + + init_core(1); + + /* main loop */ + while(1) { + struct packetcontext *p; + struct icmp6_hdr *icmph; +#if ENABLE_DEBUG + char saddrbuf[NI_MAXHOST]; + int gni; +#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 (1) { + memset(p, 0, sizeof(*p)); + p->sock = sock; + + if (ni_recv(p) < 0) { + if (errno == EAGAIN || errno == EINTR) + continue; + /* XXX: syslog */ + continue; + } + break; + } + +#if ENABLE_DEBUG + gni = getnameinfo((struct sockaddr *)&p->addr, + p->addrlen, + saddrbuf, sizeof(saddrbuf), + NULL, 0, + NI_NUMERICHOST); + if (gni) + 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 */ + } +} + 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 <yoshfuji@linux-ipv6.org> + */ + +/* 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 <yoshfuji@linux-ipv6.org> + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#if HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +#if STDC_HEADERS +# include <stdio.h> +# include <stdlib.h> +# include <stddef.h> +#else +# if HAVE_STDLIB_H +# include <stdlib.h> +# endif +#endif +#if HAVE_STRING_H +# if !STDC_HEADERS && HAVE_MEMORY_H +# include <memory.h> +# endif +# include <string.h> +#endif +#if HAVE_STRINGS_H +# include <strings.h> +#endif +#if HAVE_INTTYPES_H +# include <inttypes.h> +#else +# if HAVE_STDINT_H +# include <stdint.h> +# endif +#endif +#if HAVE_UNISTD_H +# include <unistd.h> +#endif + +#if TIME_WITH_SYS_TIME +# include <sys/time.h> +# include <time.h> +#else +# if HAVE_SYS_TIME_H +# include <sys/time.h> +# else +# include <time.h> +# endif +#endif + +#if HAVE_SYS_UIO_H +#include <sys/uio.h> +#endif + +#include <sys/socket.h> +#if HAVE_LINUX_RTNETLINK_H +#include <asm/types.h> +#include <linux/rtnetlink.h> +#endif + +#if HAVE_NETINET_IN_H +# include <netinet/in.h> +#endif + +#if HAVE_NETINET_IP6_H +# include <netinet/ip6.h> +#endif + +#if HAVE_NETINET_ICMP6_H +# include <netinet/icmp6.h> +#endif +#ifndef HAVE_STRUCT_ICMP6_NODEINFO +# include "icmp6_nodeinfo.h" +#endif + +#if HAVE_NETDB_H +# include <netdb.h> +#endif +#include <errno.h> + +#if HAVE_SYSLOG_H +# include <syslog.h> +#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..47e9e7d --- /dev/null +++ b/ninfod/ninfod_core.c @@ -0,0 +1,702 @@ +/* $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 <yoshfuji@linux-ipv6.org> + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#if HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif +#if STDC_HEADERS +# include <stdio.h> +# include <stdlib.h> +# include <stddef.h> +#else +# if HAVE_STDLIB_H +# include <stdlib.h> +# endif +#endif +#if ENABLE_THREADS && HAVE_PTHREAD_H +# include <pthread.h> +#endif +#if HAVE_STRING_H +# if !STDC_HEADERS && HAVE_MEMORY_H +# include <memory.h> +# endif +# include <string.h> +#endif +#if HAVE_STRINGS_H +# include <strings.h> +#endif +#if HAVE_INTTYPES_H +# include <inttypes.h> +#else +# if HAVE_STDINT_H +# include <stdint.h> +# endif +#endif +#if HAVE_UNISTD_H +# include <unistd.h> +#endif + +#if TIME_WITH_SYS_TIME +# include <sys/time.h> +# include <time.h> +#else +# if HAVE_SYS_TIME_H +# include <sys/time.h> +# else +# include <time.h> +# endif +#endif + +#if HAVE_SYS_UIO_H +#include <sys/uio.h> +#endif + +#if HAVE_NETINET_IN_H +# include <netinet/in.h> +#endif + +#if HAVE_NETINET_ICMP6_H +# include <netinet/icmp6.h> +#endif +#ifndef HAVE_STRUCT_ICMP6_NODEINFO +# include "icmp6_nodeinfo.h" +#endif + +#if HAVE_NETDB_H +# include <netdb.h> +#endif +#include <errno.h> + +#if HAVE_SYSLOG_H +# include <syslog.h> +#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) +{ + struct icmp6_nodeinfo *replybuf = NULL; + + 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) +{ + size_t replylen = sizeof(struct icmp6_nodeinfo) + (suptypes_len<<2); + + 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<<b); + + if (suptypes_len < w) + suptypes_len = w; + } + suptypes_len++; +} +#endif + +/* ---------- */ +/* unknown qtype response */ +int pr_nodeinfo_unknown(CHECKANDFILL_ARGS) +{ + if (!reply) + return -1; /*???*/ + + p->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..c6e7c4a --- /dev/null +++ b/ninfod/ninfod_name.c @@ -0,0 +1,387 @@ +/* $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 <yoshfuji@linux-ipv6.org> + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#if HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif +#if STDC_HEADERS +# include <stdio.h> +# include <stdlib.h> +# include <stddef.h> +# include <ctype.h> +#else +# if HAVE_STDLIB_H +# include <stdlib.h> +# endif +#endif +#if HAVE_STRING_H +# if !STDC_HEADERS && HAVE_MEMORY_H +# include <memory.h> +# endif +# include <string.h> +#endif +#if HAVE_STRINGS_H +# include <strings.h> +#endif +#if HAVE_INTTYPES_H +# include <inttypes.h> +#else +# if HAVE_STDINT_H +# include <stdint.h> +# endif +#endif +#if HAVE_UNISTD_H +# include <unistd.h> +#endif + +#if TIME_WITH_SYS_TIME +# include <sys/time.h> +# include <time.h> +#else +# if HAVE_SYS_TIME_H +# include <sys/time.h> +# else +# include <time.h> +# endif +#endif + +#if HAVE_SYS_UIO_H +#include <sys/uio.h> +#endif + +#include <sys/socket.h> + +#if HAVE_NETINET_IN_H +# include <netinet/in.h> +#endif + +#if HAVE_NETINET_ICMP6_H +# include <netinet/icmp6.h> +#endif +#ifndef HAVE_STRUCT_ICMP6_NODEINFO +# include "icmp6_nodeinfo.h" +#endif + +#include <arpa/inet.h> + +#ifdef HAVE_OPENSSL_MD5_H +#include <openssl/md5.h> +#endif + +#if HAVE_SYS_UTSNAME_H +# include <sys/utsname.h> +#endif +#if HAVE_NETDB_H +# include <netdb.h> +#endif +#include <errno.h> + +#if HAVE_SYSLOG_H +# include <syslog.h> +#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; + 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; +} + @@ -0,0 +1,1344 @@ +/* + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1989 The Regents of the University of California.\n\ + All rights reserved.\n"; +#endif /* not lint */ + +/* + * 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. + * This program has to run SUID to ROOT to access the ICMP socket. + */ + +#include "ping_common.h" + +#include <netinet/ip.h> +#include <netinet/ip_icmp.h> + +#ifndef ICMP_FILTER +#define ICMP_FILTER 1 +struct icmp_filter { + __u32 data; +}; +#endif + + +#define MAXIPLEN 60 +#define MAXICMPLEN 76 +#define NROUTES 9 /* number of record route slots */ +#define TOS_MAX 255 /* 8-bit TOS field */ +#define MAX_HOSTNAMELEN NI_MAXHOST + + +static int ts_type; +static int nroute = 0; +static __u32 route[10]; + + + +struct sockaddr_in whereto; /* who to ping */ +int optlen = 0; +int settos = 0; /* Set TOS, Precendence or other QOS options */ +int icmp_sock; /* socket file descriptor */ +u_char outpack[0x10000]; +int maxpacket = sizeof(outpack); + +static int broadcast_pings = 0; + +static char *pr_addr(__u32); +static void pr_options(unsigned char * cp, int hlen); +static void pr_iph(struct iphdr *ip); +static void usage(void) __attribute__((noreturn)); +static u_short in_cksum(const u_short *addr, int len, u_short salt); +static void pr_icmph(__u8 type, __u8 code, __u32 info, struct icmphdr *icp); +static int parsetos(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; + +struct sockaddr_in source; +char *device; +int pmtudisc = -1; + + +int +main(int argc, char **argv) +{ + struct hostent *hp; + int ch, hold, packlen; + int socket_errno; + u_char *packet; + char *target; +#ifdef USE_IDN + char *hnamebuf = NULL; +#else + char hnamebuf[MAX_HOSTNAMELEN]; +#endif + char rspace[3 + 4 * NROUTES + 1]; /* record route space */ + + limit_capabilities(); + +#ifdef USE_IDN + setlocale(LC_ALL, ""); +#endif + + enable_capability_raw(); + + icmp_sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); + socket_errno = errno; + + disable_capability_raw(); + + source.sin_family = AF_INET; + + preload = 1; + while ((ch = getopt(argc, argv, COMMON_OPTSTR "bRT:")) != EOF) { + switch(ch) { + case 'b': + broadcast_pings = 1; + break; + case 'Q': + settos = parsetos(optarg); + if (settos && + (setsockopt(icmp_sock, IPPROTO_IP, IP_TOS, + (char *)&settos, sizeof(int)) < 0)) { + perror("ping: error setting QOS sockopts"); + exit(2); + } + 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; + case 'I': + { +#if 0 + char dummy; + int i1, i2, i3, i4; + + if (sscanf(optarg, "%u.%u.%u.%u%c", + &i1, &i2, &i3, &i4, &dummy) == 4) { + __u8 *ptr; + ptr = (__u8*)&source.sin_addr; + ptr[0] = i1; + ptr[1] = i2; + ptr[2] = i3; + ptr[3] = i4; + options |= F_STRICTSOURCE; + } else { + device = optarg; + } +#else + if (inet_pton(AF_INET, optarg, &source.sin_addr) > 0) + options |= F_STRICTSOURCE; + else + device = optarg; +#endif + 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 'V': + printf("ping utility, iputils-%s\n", SNAPSHOT); + exit(0); + COMMON_OPTIONS + common_options(ch); + break; + default: + usage(); + } + } + argc -= optind; + argv += optind; + + if (argc == 0) + usage(); + 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 { + char *idn; +#ifdef USE_IDN + int rc; + + if (hnamebuf) { + free(hnamebuf); + hnamebuf = NULL; + } + + rc = idna_to_ascii_lz(target, &idn, 0); + if (rc != IDNA_SUCCESS) { + fprintf(stderr, "ping: IDN encoding failed: %s\n", idna_strerror(rc)); + exit(2); + } +#else + idn = target; +#endif + hp = gethostbyname(idn); + if (!hp) { + fprintf(stderr, "ping: unknown host %s\n", target); + exit(2); + } +#ifdef USE_IDN + free(idn); +#endif + memcpy(&whereto.sin_addr, hp->h_addr, 4); +#ifdef USE_IDN + if (idna_to_unicode_lzlz(hp->h_name, &hnamebuf, 0) != IDNA_SUCCESS) { + hnamebuf = strdup(hp->h_name); + if (!hnamebuf) { + perror("ping: strdup"); + exit(-1); + } + } +#else + strncpy(hnamebuf, hp->h_name, sizeof(hnamebuf) - 1); + hnamebuf[sizeof(hnamebuf) - 1] = 0; +#endif + hostname = hnamebuf; + } + 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 rc; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, device, IFNAMSIZ-1); + + enable_capability_raw(); + rc = setsockopt(probe_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(probe_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(probe_fd, SOL_IP, IP_MULTICAST_IF, &imr, sizeof(imr)) == -1) { + perror("ping: IP_MULTICAST_IF"); + 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\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; + close(probe_fd); + } while (0); + + if (whereto.sin_addr.s_addr == 0) + whereto.sin_addr.s_addr = source.sin_addr.s_addr; + + if (icmp_sock < 0) { + errno = socket_errno; + perror("ping: icmp open socket"); + exit(2); + } + + if (device) { + struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, device, IFNAMSIZ-1); + if (ioctl(icmp_sock, 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(icmp_sock, SOL_IP, IP_MTU_DISCOVER, &pmtudisc, sizeof(pmtudisc)) == -1) { + perror("ping: IP_MTU_DISCOVER"); + exit(2); + } + } + + if ((options&F_STRICTSOURCE) && + bind(icmp_sock, (struct sockaddr*)&source, sizeof(source)) == -1) { + perror("bind"); + exit(2); + } + + if (1) { + struct icmp_filter filt; + filt.data = ~((1<<ICMP_SOURCE_QUENCH)| + (1<<ICMP_DEST_UNREACH)| + (1<<ICMP_TIME_EXCEEDED)| + (1<<ICMP_PARAMETERPROB)| + (1<<ICMP_REDIRECT)| + (1<<ICMP_ECHOREPLY)); + if (setsockopt(icmp_sock, SOL_RAW, ICMP_FILTER, (char*)&filt, sizeof(filt)) == -1) + perror("WARNING: setsockopt(ICMP_FILTER)"); + } + + hold = 1; + if (setsockopt(icmp_sock, SOL_IP, IP_RECVERR, (char *)&hold, sizeof(hold))) + fprintf(stderr, "WARNING: your kernel is veeery old. No problems.\n"); + + /* 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(icmp_sock, 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++) + *(__u32*)&rspace[4+i*8] = route[i]; + } + if (setsockopt(icmp_sock, IPPROTO_IP, IP_OPTIONS, rspace, rspace[1]) < 0) { + rspace[3] = 2; + if (setsockopt(icmp_sock, 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++) + *(__u32*)&rspace[4+i*4] = route[i]; + + if (setsockopt(icmp_sock, 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(icmp_sock, hold); + + if (broadcast_pings) { + if (setsockopt(icmp_sock, 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(icmp_sock, IPPROTO_IP, IP_MULTICAST_LOOP, + &loop, 1) == -1) { + perror ("ping: can't disable multicast loopback"); + exit(2); + } + } + if (options & F_TTL) { + int ittl = ttl; + if (setsockopt(icmp_sock, IPPROTO_IP, IP_MULTICAST_TTL, + &ttl, 1) == -1) { + perror ("ping: can't set multicast time-to-live"); + exit(2); + } + if (setsockopt(icmp_sock, 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) { + if (uid || datalen > sizeof(outpack)-8) { + fprintf(stderr, "Error: packet size %d is too large. Maximum is %d\n", datalen, 0xFFFF-8-20-optlen); + exit(2); + } + /* Allow small oversize to root yet. It will cause EMSGSIZE. */ + fprintf(stderr, "WARNING: packet size %d is too large. Maximum is %d\n", datalen, 0xFFFF-8-20-optlen); + } + + if (datalen >= sizeof(struct timeval)) /* can we time transfer */ + timing = 1; + packlen = datalen + MAXIPLEN + MAXICMPLEN; + if (!(packet = (u_char *)malloc((u_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(icmp_sock); + + main_loop(icmp_sock, packet, packlen); +} + + +int receive_error_msg() +{ + 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(icmp_sock, &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 || + icmph.un.echo.id != ident) { + /* Not our error, not an error at all. Clear. */ + saved_errno = 0; + goto out; + } + + acknowledge(ntohs(icmph.un.echo.sequence)); + + if (!working_recverr) { + struct icmp_filter filt; + working_recverr = 1; + /* OK, it works. Add stronger filter. */ + filt.data = ~((1<<ICMP_SOURCE_QUENCH)| + (1<<ICMP_REDIRECT)| + (1<<ICMP_ECHOREPLY)); + if (setsockopt(icmp_sock, SOL_RAW, ICMP_FILTER, (char*)&filt, sizeof(filt)) == -1) + perror("\rWARNING: setsockopt(ICMP_FILTER)"); + } + + 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->sin_addr.s_addr), 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 send_probe() +{ + struct icmphdr *icp; + int cc; + int i; + + icp = (struct icmphdr *)outpack; + 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((u_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((u_short *)(icp+1), sizeof(tmp_tv), ~icp->checksum); + } + + do { + static struct iovec iov = {outpack, 0}; + static struct msghdr m = { &whereto, sizeof(whereto), + &iov, 1, &cmsg, 0, 0 }; + m.msg_controllen = cmsg_len; + iov.iov_len = cc; + + i = sendmsg(icmp_sock, &m, confirm); + confirm = 0; + } while (0); + + 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!). + */ +void pr_echo_reply(__u8 *_icp, int len) +{ + struct icmphdr *icp = (struct icmphdr *)_icp; + printf(" icmp_seq=%u", ntohs(icp->un.echo.sequence)); +} + +int +parse_reply(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; + + /* Check the IP header */ + ip = (struct iphdr *)buf; + 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->sin_addr.s_addr)); + return 1; + } + + /* Now the ICMP part */ + cc -= hlen; + icp = (struct icmphdr *)(buf + hlen); + csfailed = in_cksum((u_short *)icp, cc, 0); + + if (icp->type == ICMP_ECHOREPLY) { + if (icp->un.echo.id != ident) + return 1; /* 'Twas not our ECHO */ + if (gather_statistics((__u8*)icp, sizeof(*icp), cc, + ntohs(icp->un.echo.sequence), + ip->ttl, 0, tv, pr_addr(from->sin_addr.s_addr), + pr_echo_reply)) + return 0; + } else { + /* We fall here when a redirect or source quench arrived. + * Also this branch processes icmp errors, when IP_RECVERR + * is broken. */ + + 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 || + icp1->un.echo.id != ident) + return 1; + error_pkt = (icp->type != ICMP_REDIRECT && + icp->type != ICMP_SOURCE_QUENCH); + if (error_pkt) { + acknowledge(ntohs(icp1->un.echo.sequence)); + if (working_recverr) { + return 0; + } else { + static int once; + /* Sigh, IP_RECVERR for raw socket + * was broken until 2.4.9. So, we ignore + * the first error and warn on the second. + */ + if (once++ == 1) + fprintf(stderr, "\rWARNING: kernel is not very fresh, upgrade is recommended.\n"); + if (once == 1) + return 0; + } + } + nerrors+=error_pkt; + if (options&F_QUIET) + return !error_pkt; + if (options & F_FLOOD) { + if (error_pkt) + write_stdout("\bE", 2); + return !error_pkt; + } + print_timestamp(); + printf("From %s: icmp_seq=%u ", + pr_addr(from->sin_addr.s_addr), + ntohs(icp1->un.echo.sequence)); + if (csfailed) + printf("(BAD CHECKSUM)"); + pr_icmph(icp->type, icp->code, ntohl(icp->un.gateway), icp); + return !error_pkt; + } + 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->sin_addr.s_addr)); + if (csfailed) { + printf("(BAD CHECKSUM)\n"); + return 0; + } + pr_icmph(icp->type, icp->code, ntohl(icp->un.gateway), icp); + return 0; + } + + if (!(options & F_FLOOD)) { + pr_options(buf + sizeof(struct iphdr), hlen); + + if (options & F_AUDIBLE) + putchar('\a'); + putchar('\n'); + fflush(stdout); + } else { + putchar('\a'); + fflush(stdout); + } + return 0; +} + +u_short +in_cksum(const u_short *addr, register int len, u_short csum) +{ + register int nleft = len; + const u_short *w = addr; + register u_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 += htons(*(u_char *)w << 8); + + /* + * 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; + } + if (icp) + printf("(New nexthop: %s)\n", pr_addr(icp->un.gateway)); + 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; + i = *++cp; + i -= 4; + cp++; + if (j > IPOPT_MINOFF) { + for (;;) { + __u32 address; + memcpy(&address, cp, 4); + cp += 4; + if (address == 0) + printf("\t0.0.0.0"); + else + printf("\t%s", pr_addr(address)); + 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)"); + i = ((i + 3) / 4) * 4; + cp += i; + 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 + printf("\t%s", pr_addr(address)); + 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 + printf("\t%s", pr_addr(address)); + 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; + u_char *cp; + + hlen = ip->ihl << 2; + cp = (u_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 as a dotted quad and optionally with + * a hostname. + */ +char * +pr_addr(__u32 addr) +{ + struct hostent *hp; + static char buf[4096]; + + in_pr_addr = !setjmp(pr_addr_jmp); + + if (exiting || (options & F_NUMERIC) || + !(hp = gethostbyaddr((char *)&addr, 4, AF_INET))) + sprintf(buf, "%s", inet_ntoa(*(struct in_addr *)&addr)); + else { + char *s; +#if USE_IDN + if (idna_to_unicode_lzlz(hp->h_name, &s, 0) != IDNA_SUCCESS) + s = NULL; +#else + s = NULL; +#endif + snprintf(buf, sizeof(buf), "%s (%s)", s ? s : hp->h_name, + inet_ntoa(*(struct in_addr *)&addr)); +#if USE_IDN + free(s); +#endif + } + + in_pr_addr = 0; + + return(buf); +} + + +/* 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 0-254 (or zero)\n"); + exit(2); + } + return(tos); +} + +#include <linux/filter.h> + +void install_filter(void) +{ + 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(icmp_sock, 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" + " [-" + "aAbBdDfhLnOqrRUvV" + "]" + " [-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" + ); + exit(2); +} @@ -0,0 +1,1827 @@ +/* + * + * Modified for AF_INET6 by Pedro Roque + * + * <roque@di.fc.ul.pt> + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1989 The Regents of the University of California.\n\ + All rights reserved.\n"; +#endif /* not lint */ + +/* + * 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. + * This program has to run SUID to ROOT to access the ICMP socket. + */ +#include "ping_common.h" + +#include <linux/filter.h> +#include <netinet/ip6.h> +#include <netinet/icmp6.h> +#include <resolv.h> + +#ifdef USE_IDN +#include <stringprep.h> +#endif + +#include "ping6_niquery.h" +#include "in6_flowlabel.h" + +#ifndef SOL_IPV6 +#define SOL_IPV6 IPPROTO_IPV6 +#endif + +#ifndef SOL_ICMPV6 +#define SOL_ICMPV6 IPPROTO_ICMPV6 +#endif + +/* RFC3542 */ +#ifndef ICMP6_DST_UNREACH_BEYONDSCOPE +#define ICMP6_DST_UNREACH_BEYONDSCOPE ICMP6_DST_UNREACH_NOTNEIGHBOR +#endif + +#if defined(ENABLE_PING6_RTHDR) && !defined(ENABLE_PING6_RTHDR_RFC3542) +#ifndef IPV6_SRCRT_TYPE_0 +#define IPV6_SRCRT_TYPE_0 0 +#endif +#endif + +#ifndef MLD_LISTENER_QUERY +#define MLD_LISTENER_QUERY 130 +#define MLD_LISTENER_REPORT 131 +#define MLD_LISTENER_REDUCTION 132 +#endif + +#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 ICMP6_FILTER_WILLPASS +#define ICMP6_FILTER_WILLPASS(type, filterp) \ + (BIT_TEST((type), filterp) == 0) + +#define ICMP6_FILTER_WILLBLOCK(type, filterp) \ + BIT_TEST((type), filterp) + +#define ICMP6_FILTER_SETPASS(type, filterp) \ + BIT_CLEAR((type), filterp) + +#define ICMP6_FILTER_SETBLOCK(type, filterp) \ + BIT_SET((type), filterp) + +#define ICMP6_FILTER_SETPASSALL(filterp) \ + memset(filterp, 0, sizeof(struct icmp6_filter)); + +#define ICMP6_FILTER_SETBLOCKALL(filterp) \ + memset(filterp, 0xFF, sizeof(struct icmp6_filter)); +#endif + +#define MAXPACKET 128000 /* max packet size */ + +#ifdef SO_TIMESTAMP +#define HAVE_SIN6_SCOPEID 1 +#endif + +#ifndef SCOPE_DELIMITER +# define SCOPE_DELIMITER '%' +#endif + +__u32 flowlabel; +__u32 tclass; +#ifdef ENABLE_PING6_RTHDR +struct cmsghdr *srcrt; +#endif + +struct sockaddr_in6 whereto; /* who to ping */ +u_char outpack[MAXPACKET]; +int maxpacket = sizeof(outpack); + +static unsigned char cmsgbuf[4096]; +static int cmsglen = 0; + +static char * pr_addr(struct in6_addr *addr); +static char * pr_addr_n(struct in6_addr *addr); +static int pr_icmph(__u8 type, __u8 code, __u32 info); +static void usage(void) __attribute((noreturn)); + +struct sockaddr_in6 source; +char *device; +int pmtudisc=-1; + +static int icmp_sock; + +#include <openssl/md5.h> + +/* 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 = SOL_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) +{ + 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); + } +} +#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 * ((double)random() / RAND_MAX); + + 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, *ai0, *ai; + int offset; + int gai; + + if (niquery_set_subject_type(niquery_options[index].data) < 0) + return -1; + + ni_subject_type = niquery_options[index].data; + + memset(&hints, 0, sizeof(hints)); + + 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; + } + + hints.ai_socktype = SOCK_DGRAM; +#ifdef USE_IDN + hints.ai_flags = AI_IDN; +#endif + + gai = getaddrinfo(arg, 0, &hints, &ai0); + if (gai) { + fprintf(stderr, "Unknown host: %s\n", arg); + return -1; + } + + for (ai = ai0; 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(ai0); + + return 0; +} + +static int niquery_option_subject_name_handler(int index, const char *arg) +{ + static char nigroup_buf[INET6_ADDRSTRLEN + 1 + IFNAMSIZ]; + unsigned char *dnptrs[2], **dpp, **lastdnptr; + int n; + int i; + char *name, *p; + char *canonname, *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 + name = stringprep_locale_to_utf8(arg); + if (!name) { + fprintf(stderr, "ping6: IDN support failed.\n"); + exit(2); + } +#else + name = strdup(arg); + if (!name) + goto oomexit; +#endif + + p = strchr(name, SCOPE_DELIMITER); + if (p) { + *p = '\0'; + if (strlen(p + 1) >= IFNAMSIZ) { + fprintf(stderr, "ping6: too long scope name.\n"); + exit(1); + } + } + +#ifdef USE_IDN + rc = idna_to_ascii_8z(name, &idn, 0); + if (rc) { + fprintf(stderr, "ping6: IDN encoding error: %s\n", + idna_strerror(rc)); + exit(2); + } +#else + idn = strdup(name); + if (!idn) + goto oomexit; +#endif + + 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); + free(name); + + return 0; +oomexit: + fprintf(stderr, "ping6: out of memory.\n"); +errexit: + free(buf); + free(canonname); + free(idn); + free(name); + exit(1); +} + +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; +} + +static int hextoui(const char *str) +{ + unsigned long val; + char *ep; + + val = strtoul(str, &ep, 16); + if (*ep) { + if (!errno) + errno = EINVAL; + return -1; + } + + if (val > UINT_MAX) { + errno = ERANGE; + return UINT_MAX; + } + + return val; +} + +int main(int argc, char *argv[]) +{ + int ch, hold, packlen; + u_char *packet; + char *target; + struct addrinfo hints, *ai; + int gai; + struct sockaddr_in6 firsthop; + int socket_errno; + struct icmp6_filter filter; + int err; +#ifdef __linux__ + int csum_offset, sz_opt; +#endif + static uint32_t scope_id = 0; + + limit_capabilities(); + +#ifdef USE_IDN + setlocale(LC_ALL, ""); +#endif + + enable_capability_raw(); + + icmp_sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); + socket_errno = errno; + + disable_capability_raw(); + + source.sin6_family = AF_INET6; + memset(&firsthop, 0, sizeof(firsthop)); + firsthop.sin6_family = AF_INET6; + + preload = 1; + while ((ch = getopt(argc, argv, COMMON_OPTSTR "F:N:")) != EOF) { + switch(ch) { + case 'F': + flowlabel = hextoui(optarg); + if (errno || (flowlabel & ~IPV6_FLOWINFO_FLOWLABEL)) { + fprintf(stderr, "ping: Invalid flowinfo %s\n", optarg); + exit(2); + } + options |= F_FLOWINFO; + break; + case 'Q': + tclass = hextoui(optarg); + if (errno || (tclass & ~0xff)) { + fprintf(stderr, "ping: Invalid tclass %s\n", optarg); + exit(2); + } + options |= F_TCLASS; + break; + case 'I': + 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*)&source.sin6_addr) <= 0) { + fprintf(stderr, "ping: invalid source address %s\n", optarg); + exit(2); + } + + options |= F_STRICTSOURCE; + + free(addr); + } else { + device = optarg; + } + break; + case 'M': + if (strcmp(optarg, "do") == 0) + pmtudisc = IPV6_PMTUDISC_DO; + else if (strcmp(optarg, "dont") == 0) + pmtudisc = IPV6_PMTUDISC_DONT; + else if (strcmp(optarg, "want") == 0) + pmtudisc = IPV6_PMTUDISC_WANT; + else { + fprintf(stderr, "ping: wrong value for -M: do, dont, want are valid ones.\n"); + exit(2); + } + break; + case 'V': + printf("ping6 utility, iputils-%s\n", SNAPSHOT); + exit(0); + case 'N': + if (niquery_option_handler(optarg) < 0) { + usage(); + break; + } + break; + COMMON_OPTIONS + common_options(ch); + break; + default: + usage(); + } + } + argc -= optind; + argv += optind; + +#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; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET6; +#ifdef USE_IDN + hints.ai_flags = AI_IDN; +#endif + gai = getaddrinfo(target, NULL, &hints, &ai); + if (gai) { + fprintf(stderr, "unknown host\n"); + exit(2); + } + addr = &((struct sockaddr_in6 *)(ai->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); +#ifdef HAVE_SIN6_SCOPEID + firsthop.sin6_scope_id = ((struct sockaddr_in6 *)(ai->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; + } +#endif + } + freeaddrinfo(ai); + + 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 + usage(); + } else if (argc == 1) { + target = *argv; + } else { + if (ni_query < 0 && ni_subject_type != NI_SUBJ_NAME) + usage(); + target = ni_group; + } + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET6; +#ifdef USE_IDN + hints.ai_flags = AI_IDN; +#endif + gai = getaddrinfo(target, NULL, &hints, &ai); + if (gai) { + fprintf(stderr, "unknown host\n"); + exit(2); + } + + memcpy(&whereto, ai->ai_addr, sizeof(whereto)); + whereto.sin6_port = htons(IPPROTO_ICMPV6); + + if (memchr(target, ':', strlen(target))) + options |= F_NUMERIC; + + freeaddrinfo(ai); + + if (IN6_IS_ADDR_UNSPECIFIED(&firsthop.sin6_addr)) { + memcpy(&firsthop.sin6_addr, &whereto.sin6_addr, 16); +#ifdef HAVE_SIN6_SCOPEID + 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; + } +#endif + } + + hostname = target; + + if (IN6_IS_ADDR_UNSPECIFIED(&source.sin6_addr)) { + socklen_t alen; + int probe_fd = socket(AF_INET6, SOCK_DGRAM, 0); + + if (probe_fd < 0) { + perror("socket"); + exit(2); + } + if (device) { +#if defined(IPV6_RECVPKTINFO) || defined(HAVE_SIN6_SCOPEID) + unsigned int iface = if_name2index(device); +#endif +#ifdef IPV6_RECVPKTINFO + struct in6_pktinfo ipi; + + memset(&ipi, 0, sizeof(ipi)); + ipi.ipi6_ifindex = iface; +#endif + +#ifdef HAVE_SIN6_SCOPEID + if (IN6_IS_ADDR_LINKLOCAL(&firsthop.sin6_addr) || + IN6_IS_ADDR_MC_LINKLOCAL(&firsthop.sin6_addr)) + firsthop.sin6_scope_id = iface; +#endif + enable_capability_raw(); + if ( +#ifdef IPV6_RECVPKTINFO + setsockopt(probe_fd, IPPROTO_IPV6, IPV6_PKTINFO, &ipi, sizeof(ipi)) == -1 && +#endif + setsockopt(probe_fd, SOL_SOCKET, SO_BINDTODEVICE, device, strlen(device)+1) == -1) { + perror("setsockopt(SO_BINDTODEVICE)"); + } + disable_capability_raw(); + } + firsthop.sin6_port = htons(1025); + if (connect(probe_fd, (struct sockaddr*)&firsthop, sizeof(firsthop)) == -1) { + perror("connect"); + exit(2); + } + alen = sizeof(source); + if (getsockname(probe_fd, (struct sockaddr*)&source, &alen) == -1) { + perror("getsockname"); + exit(2); + } + source.sin6_port = 0; + close(probe_fd); + } +#ifdef HAVE_SIN6_SCOPEID + else if (device && (IN6_IS_ADDR_LINKLOCAL(&source.sin6_addr) || + IN6_IS_ADDR_MC_LINKLOCAL(&source.sin6_addr))) + source.sin6_scope_id = if_name2index(device); +#endif + + if (icmp_sock < 0) { + errno = socket_errno; + perror("ping: icmp open socket"); + exit(2); + } + + 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 = SOL_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(icmp_sock, SOL_IPV6, IPV6_MTU_DISCOVER, &pmtudisc, sizeof(pmtudisc)) == -1) { + perror("ping: IPV6_MTU_DISCOVER"); + exit(2); + } + } + + if ((options&F_STRICTSOURCE) && + bind(icmp_sock, (struct sockaddr*)&source, sizeof(source)) == -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 = (u_char *)malloc((u_int)packlen))) { + fprintf(stderr, "ping: out of memory.\n"); + exit(2); + } + + working_recverr = 1; + hold = 1; + if (setsockopt(icmp_sock, SOL_IPV6, IPV6_RECVERR, (char *)&hold, sizeof(hold))) { + fprintf(stderr, "WARNING: your kernel is veeery old. No problems.\n"); + working_recverr = 0; + } + + /* 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(icmp_sock, hold); + +#ifdef __linux__ + csum_offset = 2; + sz_opt = sizeof(int); + + err = setsockopt(icmp_sock, 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); + + if (!working_recverr) { + 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(icmp_sock, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, + sizeof(struct icmp6_filter)); + + if (err < 0) { + perror("setsockopt(ICMP6_FILTER)"); + exit(2); + } + + if (options & F_NOLOOP) { + int loop = 0; + if (setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, + &loop, sizeof(loop)) == -1) { + perror ("can't disable multicast loopback"); + exit(2); + } + } + if (options & F_TTL) { + if (setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, + &ttl, sizeof(ttl)) == -1) { + perror ("can't set multicast hop limit"); + exit(2); + } + if (setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, + &ttl, sizeof(ttl)) == -1) { + perror ("can't set unicast hop limit"); + exit(2); + } + } + + if (1) { + int on = 1; + if ( +#ifdef IPV6_RECVHOPLIMIT + setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, + &on, sizeof(on)) == -1 && + setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_2292HOPLIMIT, + &on, sizeof(on)) == -1 +#else + setsockopt(icmp_sock, 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(icmp_sock, 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_FLOWINFO_SEND + int on = 1; +#endif +#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(icmp_sock, 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(icmp_sock, 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.sin6_addr)); + if (flowlabel) + printf(", flow 0x%05x, ", (unsigned)ntohl(flowlabel)); + if (device || (options&F_STRICTSOURCE)) { + printf("from %s %s: ", + pr_addr_n(&source.sin6_addr), device ? : ""); + } + printf("%d data bytes\n", datalen); + + setup(icmp_sock); + + drop_capabilities(); + + main_loop(icmp_sock, packet, packlen); +} + +int receive_error_msg() +{ + 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(icmp_sock, &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_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 || + icmph.icmp6_id != ident) { + /* 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->sin6_addr), 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) +{ + 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 *)&outpack[8], + (struct timezone *)NULL); + + cc = datalen + 8; /* skips ICMP portion */ + + return cc; +} + + +int build_niquery(__u8 *_nih) +{ + 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 send_probe(void) +{ + int len, cc; + + rcvd_clear(ntransmitted + 1); + + if (niquery_is_enabled()) + len = build_niquery(outpack); + else + len = build_echo(outpack); + + if (cmsglen == 0) { + cc = sendto(icmp_sock, (char *)outpack, len, confirm, + (struct sockaddr *) &whereto, + sizeof(struct sockaddr_in6)); + } else { + struct msghdr mhdr; + struct iovec iov; + + iov.iov_len = len; + iov.iov_base = outpack; + + 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(icmp_sock, &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); +} + +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; + } +} + +void pr_niquery_reply_addr(struct ni_hdr *nih, int len) +{ + __u8 *h = (__u8 *)(nih + 1); + __u8 *p = h + 4; + __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)"); +} + +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 +parse_reply(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 != SOL_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 (icmph->icmp6_id != ident) + return 1; + if (gather_statistics((__u8*)icmph, sizeof(*icmph), cc, + ntohs(icmph->icmp6_seq), + hops, 0, tv, pr_addr(&from->sin6_addr), + pr_echo_reply)) + 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->sin6_addr), + 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 || + icmph1->icmp6_id != ident) + return 1; + acknowledge(ntohs(icmph1->icmp6_seq)); + if (working_recverr) + return 0; + nerrors++; + if (options & F_FLOOD) { + write_stdout("\bE", 2); + return 0; + } + print_timestamp(); + printf("From %s: icmp_seq=%u ", pr_addr(&from->sin6_addr), 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->sin6_addr)); + } + pr_icmph(icmph->icmp6_type, icmph->icmp6_code, ntohl(icmph->icmp6_mtu)); + } + + if (!(options & F_FLOOD)) { + if (options & F_AUDIBLE) + putchar('\a'); + putchar('\n'); + fflush(stdout); + } else { + putchar('\a'); + 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"); + + } + return 0; +} + +#include <linux/filter.h> + +void install_filter(void) +{ + 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(icmp_sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter))) + perror("WARNING: failed to install socket filter\n"); +} + + +/* + * pr_addr -- + * Return an ascii host address as a dotted quad and optionally with + * a hostname. + */ +char * pr_addr(struct in6_addr *addr) +{ + struct hostent *hp = NULL; + static char *s; + +#ifdef USE_IDN + free(s); +#endif + + in_pr_addr = !setjmp(pr_addr_jmp); + + if (!(exiting || options&F_NUMERIC)) + hp = gethostbyaddr((__u8*)addr, sizeof(struct in6_addr), AF_INET6); + + in_pr_addr = 0; + + if (!hp +#ifdef USE_IDN + || idna_to_unicode_lzlz(hp->h_name, &s, 0) != IDNA_SUCCESS +#endif + ) + s = NULL; + + return hp ? (s ? s : hp->h_name) : pr_addr_n(addr); +} + +char * pr_addr_n(struct in6_addr *addr) +{ + static char str[64]; + inet_ntop(AF_INET6, addr, str, sizeof(str)); + return str; +} + +#define USAGE_NEWLINE "\n " + +void usage(void) +{ + fprintf(stderr, + "Usage: ping6" + " [-" + "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" + ); + exit(2); +} diff --git a/ping6_niquery.h b/ping6_niquery.h new file mode 100644 index 0000000..fa6624a --- /dev/null +++ b/ping6_niquery.h @@ -0,0 +1,49 @@ +#include <asm/byteorder.h> + +#define NI_NONCE_SIZE 8 + +/* Node Information Query */ +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/ping_common.c b/ping_common.c new file mode 100644 index 0000000..3f8cbb7 --- /dev/null +++ b/ping_common.c @@ -0,0 +1,1062 @@ +#include "ping_common.h" +#include <ctype.h> +#include <sched.h> +#include <math.h> + +int options; + +int mark; +int sndbuf; +int ttl; +int rtt; +int rtt_addend; +__u16 acked; + +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; +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; +/* And this is workaround for bug in IP_RECVERR on raw sockets which is present + * in linux-2.2.[0-19], linux-2.4.[0-7] */ +int working_recverr; + +/* 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])) + +void limit_capabilities(void) +{ +#ifdef CAPABILITIES + cap_t cap_p; + const cap_value_t caps[] = { + CAP_NET_ADMIN, + CAP_NET_RAW, + }; + int i; + + cap_p = cap_init(); + if (!cap_p) { + perror("ping: cap_get_proc"); + exit(-1); + } + + for (i = 0; i < ARRAY_SIZE(caps); i++) { + if (cap_clear(cap_p) < 0) { + perror("ping: cap_clear"); + exit(-1); + } + + if (cap_set_flag(cap_p, CAP_PERMITTED, ARRAY_SIZE(caps) - i, caps + i, CAP_SET) < 0) { + perror("ping: cap_set_flag"); + exit(-1); + } + + if (cap_set_proc(cap_p) < 0) + continue; + + break; + } + + if (i == ARRAY_SIZE(caps)) { + perror("ping: cap_set_proc"); + if (errno != EPERM) + 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); +#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(); + + if (!cap_p) { + perror("ping: cap_get_proc"); + return -1; + } + + if (cap_set_flag(cap_p, CAP_EFFECTIVE, 1, &cap, on) < 0) { + perror("ping: cap_set_flag"); + return -1; + } + + if (cap_set_proc(cap_p) < 0) { + perror("ping: cap_set_proc"); + return -1; + } + + if (cap_free(cap_p) < 0) { + perror("ping: cap_free"); + return -1; + } + + return 0; +} +#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. + */ +static void fill(char *patp) +{ + int ii, jj, kk; + int pat[16]; + char *cp; + u_char *bp = outpack+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 <= maxpacket - (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 +} + +void common_options(int ch) +{ + switch(ch) { + case 'a': + options |= F_AUDIBLE; + break; + case 'A': + options |= F_ADAPTIVE; + 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': /* wait between sending packets */ + { + double dbl; + char *ep; + + errno = 0; + dbl = strtod(optarg, &ep); + + 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 '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 'w': + deadline = atoi(optarg); + if (deadline < 0) { + fprintf(stderr, "ping: bad wait time.\n"); + exit(2); + } + 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 'O': + options |= F_OUTSTANDING; + break; + case 'S': + sndbuf = atoi(optarg); + if (sndbuf <= 0) { + fprintf(stderr, "ping: bad sndbuf value.\n"); + exit(2); + } + break; + case 'f': + options |= F_FLOOD; + setbuf(stdout, (char *)NULL); + /* fallthrough to numeric - avoid gethostbyaddr during flood */ + case 'n': + options |= F_NUMERIC; + break; + case 'p': /* fill buffer with user pattern */ + options |= F_PINGFILLED; + fill(optarg); + break; + case 'q': + options |= F_QUIET; + break; + case 'r': + options |= F_SO_DONTROUTE; + break; + case 's': /* size of packet to send */ + 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 'v': + options |= F_VERBOSE; + break; + case 'L': + options |= F_NOLOOP; + 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 'B': + options |= F_STRICTSOURCE; + 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; + case 'V': + printf("ping utility, iputils-%s\n", SNAPSHOT); + exit(0); + default: + abort(); + } +} + + +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(void) +{ + 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 = send_probe(); + + 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=receive_error_msg()) > 0) { + /* An ICMP error arrived. */ + tokens += interval; + return MININTERVAL; + } + /* Compatibility with old linuces. */ + if (i == 0 && confirm_flag && errno == EINVAL) { + confirm_flag = 0; + errno = 0; + } + if (!errno) + goto resend; + } + + /* 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(int icmp_sock, int alloc) +{ + int rcvbuf, hold; + socklen_t tmplen = sizeof(hold); + + if (!sndbuf) + sndbuf = alloc; + setsockopt(icmp_sock, SOL_SOCKET, SO_SNDBUF, (char *)&sndbuf, sizeof(sndbuf)); + + rcvbuf = hold = alloc * preload; + if (hold < 65536) + hold = 65536; + setsockopt(icmp_sock, SOL_SOCKET, SO_RCVBUF, (char *)&hold, sizeof(hold)); + if (getsockopt(icmp_sock, 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(int icmp_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(icmp_sock, SOL_SOCKET, SO_DEBUG, (char *)&hold, sizeof(hold)); + if (options & F_SO_DONTROUTE) + setsockopt(icmp_sock, SOL_SOCKET, SO_DONTROUTE, (char *)&hold, sizeof(hold)); + +#ifdef SO_TIMESTAMP + if (!(options&F_LATENCY)) { + int on = 1; + if (setsockopt(icmp_sock, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(on))) + fprintf(stderr, "Warning: no SO_TIMESTAMP support, falling back to SIOCGSTAMP\n"); + } +#endif + if (options & F_MARK) { + int ret; + + enable_capability_admin(); + ret = setsockopt(icmp_sock, 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); + } + } + + /* 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(icmp_sock, 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(icmp_sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&tv, sizeof(tv))) + options |= F_FLOOD_POLL; + + if (!(options & F_PINGFILLED)) { + int i; + u_char *p = outpack+8; + + /* Do not forget about case of small datalen, + * fill timestamp area too! + */ + for (i = 0; i < datalen; ++i) + *p++ = i; + } + + 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; + } + } +} + +void main_loop(int icmp_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; + + 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(); + 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; + if ((options & (F_ADAPTIVE|F_FLOOD_POLL)) || next<SCHINT(interval)) { + int recv_expected = in_flight(); + + /* If we are here, recvmsg() is unable to wait for + * required timeout. */ + if (1000 % HZ == 0 ? next <= 1000 / HZ : (next < INT_MAX / HZ && next * HZ <= 1000)) { + /* Very short timeout... So, if we wait for + * something, we sleep for MININTERVAL. + * Otherwise, spin! */ + if (recv_expected) { + next = MININTERVAL; + } else { + next = 0; + /* When spinning, no reasons to poll. + * Use nonblocking recvmsg() instead. */ + polling = MSG_DONTWAIT; + /* But yield yet. */ + sched_yield(); + } + } + + if (!polling && + ((options & (F_ADAPTIVE|F_FLOOD_POLL)) || interval)) { + struct pollfd pset; + pset.fd = icmp_sock; + pset.events = POLLIN|POLLERR; + pset.revents = 0; + if (poll(&pset, 1, next) < 1 || + !(pset.revents&(POLLIN|POLLERR))) + continue; + polling = MSG_DONTWAIT; + } + } + + 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(icmp_sock, &msg, polling); + polling = MSG_DONTWAIT; + + if (cc < 0) { + if (errno == EAGAIN || errno == EINTR) + break; + if (!receive_error_msg()) { + 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(icmp_sock, SIOCGSTAMP, &recv_time)) + gettimeofday(&recv_time, NULL); + recv_timep = &recv_time; + } + + not_ours = parse_reply(&msg, cc, addrbuf, recv_timep); + } + + /* See? ... someone runs another ping on this host. */ + if (not_ours) + install_filter(); + + /* 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/1000); + else if (triptime >= 10000) + printf(" time=%ld.%01ld ms", triptime/1000, + (triptime%1000)/100); + else if (triptime >= 1000) + printf(" time=%ld.%02ld ms", triptime/1000, + (triptime%1000)/10); + else + printf(" time=%ld.%03ld ms", triptime/1000, + triptime%1000); + } + if (dupflag) + printf(" (DUP!)"); + if (csfailed) + printf(" (BAD CHECKSUM!)"); + + /* 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) { + printf("\nwrong data byte #%d should be 0x%x but was 0x%x", + i, *dp, *cp); + cp = (u_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) { + printf(", %d%% packet loss", + (int) ((((long long)(ntransmitted - nreceived)) * 100) / + ntransmitted)); + printf(", time %ldms", 1000*tv.tv_sec+tv.tv_usec/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", ntransmitted, nreceived, 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"); +} + diff --git a/ping_common.h b/ping_common.h new file mode 100644 index 0000000..27ae0f0 --- /dev/null +++ b/ping_common.h @@ -0,0 +1,288 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <time.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <linux/sockios.h> +#include <sys/file.h> +#include <sys/time.h> +#include <sys/signal.h> +#include <sys/ioctl.h> +#include <net/if.h> +#include <sys/uio.h> +#include <sys/poll.h> +#include <ctype.h> +#include <errno.h> +#include <string.h> +#include <netdb.h> +#include <setjmp.h> + +#ifdef CAPABILITIES +#include <sys/prctl.h> +#include <sys/capability.h> +#endif + +#ifdef USE_IDN +#include <locale.h> +#include <idna.h> +#endif + +#include <netinet/in.h> +#include <arpa/inet.h> +#include <linux/types.h> +#include <linux/errqueue.h> + +#include "SNAPSHOT.h" + +#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_FLOWINFO 0x200 +#define F_SOURCEROUTE 0x400 +#define F_TCLASS 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 + +/* + * 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 u_char outpack[]; +extern int maxpacket; + +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 int working_recverr; + +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; + +#define COMMON_OPTIONS \ +case 'a': case 'U': case 'c': case 'd': \ +case 'f': case 'i': case 'w': case 'l': \ +case 'S': case 'n': case 'p': case 'q': \ +case 'r': case 's': case 'v': case 'L': \ +case 't': case 'A': case 'W': case 'B': case 'm': \ +case 'D': case 'O': + +#define COMMON_OPTSTR "h?VQ:I:M:aUc:dfi:w:l:S:np:qrs:vLt:AW:Bm:DO" + +/* + * 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); + +extern int send_probe(void); +extern int receive_error_msg(void); +extern int parse_reply(struct msghdr *msg, int len, void *addr, struct timeval *); +extern void install_filter(void); + +extern int pinger(void); +extern void sock_setbufs(int icmp_sock, int alloc); +extern void setup(int icmp_sock); +extern void main_loop(int icmp_sock, __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); @@ -0,0 +1,724 @@ +/* + * 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, <kuznet@ms2.inr.ac.ru> + */ + +#include <stdio.h> +#include <syslog.h> +#include <dirent.h> +#include <malloc.h> +#include <string.h> +#include <unistd.h> +#include <stdlib.h> +#include <netdb.h> +#include <arpa/inet.h> +#include <sys/ioctl.h> +#include <sys/poll.h> +#include <sys/errno.h> +#include <sys/fcntl.h> +#include <sys/socket.h> +#include <sys/signal.h> +#include <linux/if.h> +#include <linux/if_arp.h> +#include <netinet/in.h> +#include <linux/if_packet.h> +#include <linux/filter.h> + +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 = (caddr_t)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 (n<sizeof(*a)) { + syslog(LOG_ERR, "truncated arp packet; len=%d", n); + return; + } + + /* Accept only RARP requests */ + if (a->ar_op != htons(ARPOP_RREQUEST)) + return; + + if (verbose) { + int i; + char tmpbuf[16*3]; + char *ptr = tmpbuf; + for (i=0; i<sll.sll_halen; i++) { + if (i) { + sprintf(ptr, ":%02x", sll.sll_addr[i]); + ptr++; + } else + sprintf(ptr, "%02x", sll.sll_addr[i]); + ptr += 2; + } + syslog(LOG_INFO, "RARP request from %s on if%d", tmpbuf, sll.sll_ifindex); + } + + /* Sanity checks */ + + /* 1. IP only -> 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<psize; i++) { + if (pset[i].revents&EVENTS) + serve_it(pset[i].fd); + } + } +} @@ -0,0 +1,1528 @@ +/* + * Rdisc (this program) was developed by Sun Microsystems, Inc. and is + * provided for unrestricted use provided that this legend is included on + * all tape media and as a part of the software program in whole or part. + * Users may copy or modify Rdisc without charge, and they may freely + * distribute it. + * + * RDISC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Rdisc is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY RDISC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#include <stdio.h> +#include <errno.h> +#include <signal.h> +#include <unistd.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/time.h> +/* Do not use "improved" glibc version! */ +#include <linux/limits.h> + +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/file.h> +#include <malloc.h> + +#include <sys/ioctl.h> +#include <linux/if.h> +#include <linux/route.h> + +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/ip_icmp.h> + +/* + * 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 <netdb.h> +#include <arpa/inet.h> + +#include <string.h> +#include <syslog.h> + +#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 +{ + u_char icmp_type; /* type of message, see below */ + u_char icmp_code; /* type sub code */ + u_short icmp_cksum; /* ones complement cksum of struct */ + u_char icmp_num_addrs; + u_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 <preference>] [-T <secs>]\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 u_short in_cksum(u_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 (;;) { + u_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 u_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( (u_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 u_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( (u_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 hostent *phe; + static char buf[80]; + + phe = gethostbyaddr((char *)&addr.s_addr, 4, AF_INET); + if (phe == NULL) + return( inet_ntoa(addr)); + snprintf(buf, sizeof(buf), "%s (%s)", phe->h_name, 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((u_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((u_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) + * + */ +u_short in_cksum(u_short *addr, int len) +{ + register int nleft = len; + register u_short *w = addr; + register u_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 += htons(*(u_char *)w<<8); + + /* + * 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) + interfaces = (struct interface *)ALLIGN(realloc((char *)interfaces, + numifs * sizeof(struct interface))); + else + 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; + u_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)); +} @@ -0,0 +1,89 @@ +/* + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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. + * + * @(#)tftp.h 8.1 (Berkeley) 6/2/93 + */ + +#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 */ @@ -0,0 +1,537 @@ +/* + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1983 Regents of the University of California.\n\ + All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +/*static char sccsid[] = "from: @(#)tftpd.c 5.13 (Berkeley) 2/26/91";*/ +/*static char rcsid[] = "$Id: tftpd.c,v 1.3 1993/08/01 18:28:53 mycroft Exp $";*/ +#endif /* not lint */ + +/* + * Trivial file transfer protocol server. + * + * This version includes many modifications by Jim Guyton <guyton@rand-unix> + */ + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <unistd.h> +#include <signal.h> +#include <fcntl.h> + +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> + +#include <setjmp.h> +#include <syslog.h> +#include <stdio.h> +#include <errno.h> +#include <ctype.h> +#include <string.h> +#include <stdlib.h> + +#include "tftp.h" + +#ifndef MSG_CONFIRM +#define MSG_CONFIRM 0 +#warning Please, upgrade kernel, otherwise this tftpd has no advantages. +#endif + +#define TIMEOUT 5 + +int peer; +int rexmtval = TIMEOUT; +int maxtimeout = 5*TIMEOUT; + +#define PKTSIZE SEGSIZE+4 +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; + + /* Sanity. If parent forgot to setuid() on us. */ + if (geteuid() == 0) { + setgid(65534); + setuid(65534); + } + + ac--; av++; + while (ac-- > 0 && n < MAXARG) + dirs[n++] = *av++; + + openlog("tftpd", LOG_PID, LOG_DAEMON); + 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((u_short)DATA); + dp->th_block = htons((u_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((u_short)ap->th_opcode); + ap->th_block = ntohs((u_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((u_short)ACK); + ap->th_block = htons((u_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((u_short)dp->th_opcode); + dp->th_block = ntohs((u_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((u_short)ACK); /* send the "final" ack */ + ap->th_block = htons((u_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((u_short)ERROR); + tp->th_code = htons((u_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..d362797 --- /dev/null +++ b/tftpsubs.c @@ -0,0 +1,251 @@ +/* + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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. + */ + +#ifndef lint +/*static char sccsid[] = "from: @(#)tftpsubs.c 5.6 (Berkeley) 2/28/91";*/ +/* static char rcsid[] = "$Id: tftpsubs.c,v 1.2 1993/08/01 18:07:04 mycroft Exp $"; */ +#endif /* not lint */ + +/* 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 <sys/types.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <netinet/in.h> +#include <unistd.h> +#include <stdio.h> + +#include "tftp.h" + +#define PKTSIZE SEGSIZE+4 /* should be moved to 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..1c3ca02 --- /dev/null +++ b/tracepath.c @@ -0,0 +1,438 @@ +/* + * 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, <kuznet@ms2.inr.ac.ru> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/socket.h> +#include <linux/types.h> +#include <linux/errqueue.h> +#include <errno.h> +#include <string.h> +#include <netdb.h> +#include <netinet/in.h> +#include <resolv.h> +#include <sys/time.h> +#include <sys/uio.h> +#include <arpa/inet.h> +#ifdef USE_IDN +#include <idna.h> +#include <locale.h> +#endif + +#ifndef IP_PMTUDISC_PROBE +#define IP_PMTUDISC_PROBE 3 +#endif + +struct hhistory +{ + int hops; + struct timeval sendtime; +}; + +struct hhistory his[64]; +int hisptr; + +struct sockaddr_in target; +__u16 base_port; + +const int overhead = 28; +int mtu = 65535; +int hops_to = -1; +int hops_from = -1; +int no_resolve = 0; +int show_both = 0; + +#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 = 0; + printf("%s", a); + plen = strlen(a); + if (both) { + printf(" (%s)", b); + plen += strlen(b) + 3; + } + if (plen >= HOST_COLUMN_SIZE) + plen = HOST_COLUMN_SIZE - 1; + printf("%*s", HOST_COLUMN_SIZE - plen, ""); +} + +int recverr(int fd, 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_in addr; + struct timeval tv; + struct timeval *rettv; + int slot; + int rethops; + int sndhops; + int progress = -1; + int broken_router; + +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 = ntohs(addr.sin_port) - base_port; + 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)) { + if (cmsg->cmsg_level == SOL_IP) { + if (cmsg->cmsg_type == IP_RECVERR) { + e = (struct sock_extended_err *) CMSG_DATA(cmsg); + } else if (cmsg->cmsg_type == IP_TTL) { + memcpy(&rethops, CMSG_DATA(cmsg), sizeof(rethops)); + } else { + printf("cmsg:%d\n ", cmsg->cmsg_type); + } + } + } + if (e == NULL) { + printf("no info\n"); + return 0; + } + if (e->ee_origin == SO_EE_ORIGIN_LOCAL) { + printf("%2d?: %*s ", ttl, -(HOST_COLUMN_SIZE - 1), "[LOCALHOST]"); + } else if (e->ee_origin == SO_EE_ORIGIN_ICMP) { + char abuf[128]; + struct sockaddr_in *sin = (struct sockaddr_in*)(e+1); + struct hostent *h = NULL; + char *idn = NULL; + + inet_ntop(AF_INET, &sin->sin_addr, abuf, sizeof(abuf)); + + if (sndhops>0) + printf("%2d: ", sndhops); + else + printf("%2d?: ", ttl); + + if (!no_resolve || show_both) { + fflush(stdout); + h = gethostbyaddr((char *) &sin->sin_addr, sizeof(sin->sin_addr), AF_INET); + } + +#ifdef USE_IDN + if (h && idna_to_unicode_lzlz(h->h_name, &idn, 0) != IDNA_SUCCESS) + idn = NULL; +#endif + if (no_resolve) + print_host(abuf, h ? (idn ? idn : h->h_name) : abuf, show_both); + else + print_host(h ? (idn ? idn : h->h_name) : abuf, abuf, show_both); + +#ifdef USE_IDN + free(idn); +#endif + } + + 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) "); + } + + 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) { + if (rethops>=0) { + if (rethops<=64) + rethops = 65-rethops; + else if (rethops<=128) + rethops = 129-rethops; + else + rethops = 256-rethops; + 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, int ttl) +{ + int i; + char sndbuf[mtu]; + struct probehdr *hdr = (struct probehdr*)sndbuf; + + memset(sndbuf,0,mtu); + +restart: + for (i=0; i<10; i++) { + int res; + + hdr->ttl = ttl; + target.sin_port = htons(base_port + hisptr); + gettimeofday(&hdr->tv, NULL); + his[hisptr].hops = ttl; + his[hisptr].sendtime = hdr->tv; + if (sendto(fd, sndbuf, mtu-overhead, 0, (struct sockaddr*)&target, sizeof(target)) > 0) + break; + res = recverr(fd, 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, sndbuf, sizeof(sndbuf), MSG_DONTWAIT) > 0) { + printf("%2d?: reply received 8)\n", ttl); + return 0; + } + return recverr(fd, ttl); + } + + printf("%2d: send failed\n", ttl); + return 0; +} + +static void usage(void) __attribute((noreturn)); + +static void usage(void) +{ + fprintf(stderr, "Usage: tracepath [-n] [-b] [-l <len>] [-p port] <destination>\n"); + exit(-1); +} + +int +main(int argc, char **argv) +{ + struct hostent *he; + int fd; + int on; + int ttl; + char *p; + int ch; +#ifdef USE_IDN + int rc; + setlocale(LC_ALL, ""); +#endif + + while ((ch = getopt(argc, argv, "nbh?l:p:")) != EOF) { + switch(ch) { + case 'n': + no_resolve = 1; + break; + case 'b': + show_both = 1; + break; + case 'l': + if ((mtu = atoi(optarg)) <= overhead) { + fprintf(stderr, "Error: length must be >= %d\n", overhead); + exit(1); + } + break; + case 'p': + base_port = atoi(optarg); + break; + default: + usage(); + } + } + + argc -= optind; + argv += optind; + + if (argc != 1) + usage(); + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + perror("socket"); + exit(1); + } + target.sin_family = AF_INET; + + /* Backward compatiblity */ + if (!base_port) { + p = strchr(argv[0], '/'); + if (p) { + *p = 0; + base_port = atoi(p+1); + } else + base_port = 44444; + } + + p = argv[0]; +#ifdef USE_IDN + rc = idna_to_ascii_lz(argv[0], &p, 0); + if (rc != IDNA_SUCCESS) { + fprintf(stderr, "IDNA encoding failed: %s\n", idna_strerror(rc)); + exit(2); + } +#endif + + he = gethostbyname(p); + if (he == NULL) { + herror("gethostbyname"); + exit(1); + } + +#ifdef USE_IDN + free(p); +#endif + + memcpy(&target.sin_addr, he->h_addr, 4); + + on = IP_PMTUDISC_PROBE; + if (setsockopt(fd, SOL_IP, IP_MTU_DISCOVER, &on, sizeof(on)) && + (on = IP_PMTUDISC_DO, + 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); + } + + for (ttl=1; ttl<32; ttl++) { + int res; + int i; + + on = ttl; + 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, 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: + 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); +} diff --git a/tracepath6.c b/tracepath6.c new file mode 100644 index 0000000..8a17acf --- /dev/null +++ b/tracepath6.c @@ -0,0 +1,561 @@ +/* + * tracepath6.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, <kuznet@ms2.inr.ac.ru> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/icmp6.h> + +#include <linux/types.h> +#include <linux/errqueue.h> +#include <errno.h> +#include <string.h> +#include <netdb.h> +#include <resolv.h> +#include <sys/time.h> +#include <sys/uio.h> +#include <arpa/inet.h> + +#ifdef USE_IDN +#include <idna.h> +#include <locale.h> +#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 + +struct hhistory +{ + int hops; + struct timeval sendtime; +}; + +struct hhistory his[64]; +int hisptr; + +sa_family_t family = AF_INET6; +struct sockaddr_storage target; +socklen_t targetlen; +__u16 base_port; + +int overhead = 48; +int mtu = 128000; +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 = 0; + printf("%s", a); + plen = strlen(a); + if (both) { + printf(" (%s)", b); + plen += strlen(b) + 3; + } + if (plen >= HOST_COLUMN_SIZE) + plen = HOST_COLUMN_SIZE - 1; + printf("%*s", HOST_COLUMN_SIZE - plen, ""); +} + +int recverr(int fd, 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; + +restart: + memset(&rcvbuf, -1, sizeof(rcvbuf)); + iov.iov_base = &rcvbuf; + iov.iov_len = sizeof(rcvbuf); + msg.msg_name = (caddr_t)&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 (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], hbuf[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, + hbuf, sizeof(hbuf), NULL, 0, + 0 +#ifdef USE_IDN + | NI_IDN +#endif + )) + strcpy(hbuf, "???"); + } else + hbuf[0] = 0; + + if (no_resolve) + print_host(abuf, hbuf, show_both); + else + print_host(hbuf, 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) "); + } + + 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 (rethops<=64) + rethops = 65-rethops; + else if (rethops<=128) + rethops = 129-rethops; + else + rethops = 256-rethops; + 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, int ttl) +{ + int i; + char sndbuf[mtu]; + struct probehdr *hdr = (struct probehdr*)sndbuf; + + memset(sndbuf, 0, mtu); + +restart: + + for (i=0; i<10; i++) { + int res; + + hdr->ttl = ttl; + switch (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, sndbuf, mtu-overhead, 0, (struct sockaddr *)&target, targetlen) > 0) + break; + res = recverr(fd, 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, sndbuf, sizeof(sndbuf), MSG_DONTWAIT) > 0) { + printf("%2d?: reply received 8)\n", ttl); + return 0; + } + return recverr(fd, ttl); + } + + printf("%2d: send failed\n", ttl); + return 0; +} + +static void usage(void) __attribute((noreturn)); + +static void usage(void) +{ + fprintf(stderr, "Usage: tracepath6 [-n] [-b] [-l <len>] [-p port] <destination>\n"); + exit(-1); +} + + +int main(int argc, char **argv) +{ + int fd; + int on; + int ttl; + char *p; + struct addrinfo hints, *ai, *ai0; + int ch; + int gai; + char pbuf[NI_MAXSERV]; + +#ifdef USE_IDN + setlocale(LC_ALL, ""); +#endif + + while ((ch = getopt(argc, argv, "nbh?l:p:")) != EOF) { + switch(ch) { + case 'n': + no_resolve = 1; + break; + case 'b': + show_both = 1; + break; + case 'l': + if ((mtu = atoi(optarg)) <= overhead) { + fprintf(stderr, "Error: length must be >= %d\n", overhead); + exit(1); + } + 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 = (unsigned)atoi(p+1); + } else { + base_port = 44444; + } + } + sprintf(pbuf, "%u", base_port); + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = family; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; +#ifdef USE_IDN + hints.ai_flags = AI_IDN; +#endif + gai = getaddrinfo(argv[0], pbuf, &hints, &ai0); + if (gai) { + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(gai)); + exit(1); + } + + fd = -1; + for (ai = ai0; ai; ai = ai->ai_next) { + /* sanity check */ + if (family && ai->ai_family != family) + continue; + family = ai->ai_family; + fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (fd < 0) + continue; + memcpy(&target, ai->ai_addr, sizeof(target)); + targetlen = ai->ai_addrlen; + break; + } + if (fd < 0) { + perror("socket/connect"); + exit(1); + } + freeaddrinfo(ai0); + + switch (family) { + case AF_INET6: + mtu = 128000; + overhead = 48; + 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: + mtu = 65535; + overhead = 28; + 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); + } + } + + for (ttl=1; ttl<32; ttl++) { + int res; + int i; + + on = ttl; + switch (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, 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: + 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); +} diff --git a/traceroute6.c b/traceroute6.c new file mode 100644 index 0000000..0538d4b --- /dev/null +++ b/traceroute6.c @@ -0,0 +1,950 @@ +/* + * 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 <roque@di.fc.ul.pt> + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1990, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +/* + * 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). 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 <sys/param.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <sys/file.h> +#include <sys/ioctl.h> +#include <net/if.h> + +#if __linux__ +#include <endian.h> +#endif +#include <netinet/in_systm.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/ip_icmp.h> +#include <netinet/udp.h> + +#include <netinet/ip6.h> +#include <netinet/icmp6.h> +#include <linux/types.h> +#ifdef CAPABILITIES +#include <sys/capability.h> +#endif + +#ifdef USE_IDN +#include <idna.h> +#include <locale.h> +#endif + +#include <arpa/inet.h> + +#include <netdb.h> +#include <stdio.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "SNAPSHOT.h" + +#ifndef SOL_IPV6 +#define SOL_IPV6 IPPROTO_IPV6 +#endif + +#define MAXPACKET 65535 +#define MAX_HOSTNAMELEN NI_MAXHOST + +#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 + +u_char packet[512]; /* last inbound (icmp) packet */ + +int wait_for_reply(int, struct sockaddr_in6 *, struct in6_addr *, int); +int packet_ok(u_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; +u_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[MAX_HOSTNAMELEN]; + extern char *optarg; + extern int optind; + struct hostent *hp; + struct sockaddr_in6 from, *to; + int ch, i, on, probe, seq, tos, ttl; + int socket_errno; + + 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; + to->sin6_port = htons(port); + + if (inet_pton(AF_INET6, *argv, &to->sin6_addr) > 0) { + hostname = *argv; + } else { + char *idn = NULL; +#ifdef USE_IDN + if (idna_to_ascii_lz(*argv, &idn, 0) != IDNA_SUCCESS) + idn = NULL; +#endif + hp = gethostbyname2(idn ? idn : *argv, AF_INET6); + if (hp) { + memmove((caddr_t)&to->sin6_addr, hp->h_addr, sizeof(to->sin6_addr)); + hostname = (char *)hp->h_name; + } else { + (void)fprintf(stderr, + "traceroute: unknown host %s\n", *argv); + exit(1); + } + } + 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))) { + reset_timer = 1; + 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(" !S"); + break; + } + break; + } else + reset_timer = 0; + } + if (cc <= 0) + Printf(" *"); + (void) fflush(stdout); + } + putchar('\n'); + if (got_there || + (unreachable > 0 && unreachable >= nprobes-1)) + exit(0); + } + + 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(u_char *buf, int cc, struct sockaddr_in6 *from, + struct in6_addr *to, int seq, + struct timeval *tv) +{ + struct icmp6_hdr *icp; + u_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[MAX_HOSTNAMELEN]; + char pa2[MAX_HOSTNAMELEN]; + 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[MAX_HOSTNAMELEN]; + + if (nflag) + Printf(" %s", inet_ntop(AF_INET6, &from->sin6_addr, + pa, sizeof(pa))); + else + { + const char *hostname; + struct hostent *hp; + char *s = NULL; + + hostname = inet_ntop(AF_INET6, &from->sin6_addr, pa, sizeof(pa)); + + if ((hp = gethostbyaddr((char *)&from->sin6_addr, + sizeof(from->sin6_addr), AF_INET6))) { +#ifdef USE_IDN + if (idna_to_unicode_lzlz(hp->h_name, &s, 0) != IDNA_SUCCESS) + s = NULL; +#endif + } + + Printf(" %s (%s)", hp ? (s ? s : hp->h_name) : hostname, pa); + + free(s); + } +} + + +/* + * 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); +} |