summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorshin <shin@FreeBSD.org>1999-12-28 02:37:14 +0000
committershin <shin@FreeBSD.org>1999-12-28 02:37:14 +0000
commit8c2ccb59caf882ac518eda1f570ea731d4466216 (patch)
treeec4d4f8bd121d60c04c8119810151af20c92b20b
parentb39a79861ddfb250e941d89c255bcb8cda9481dc (diff)
downloadFreeBSD-src-8c2ccb59caf882ac518eda1f570ea731d4466216.zip
FreeBSD-src-8c2ccb59caf882ac518eda1f570ea731d4466216.tar.gz
Getaddrinfo(), getnameinfo(), and etc support in libc/net.
Several udp and raw apps IPv6 support. Reviewed by: freebsd-arch, cvs-committers Obtained from: KAME project
-rw-r--r--include/netdb.h77
-rw-r--r--include/resolv.h19
-rw-r--r--lib/libc/net/Makefile.inc14
-rw-r--r--lib/libc/net/getaddrinfo.3408
-rw-r--r--lib/libc/net/getaddrinfo.c1014
-rw-r--r--lib/libc/net/getipnodebyname.3446
-rw-r--r--lib/libc/net/getnameinfo.3232
-rw-r--r--lib/libc/net/getnameinfo.c228
-rw-r--r--lib/libc/net/name6.c1260
-rw-r--r--lib/libc/net/res_init.c59
-rw-r--r--sbin/Makefile2
-rw-r--r--sbin/ping6/Makefile25
-rw-r--r--sbin/ping6/ping6.8332
-rw-r--r--sbin/ping6/ping6.c1549
-rw-r--r--sbin/rtsol/Makefile29
-rw-r--r--sys/netinet6/in6_prefix.c2
-rw-r--r--usr.bin/netstat/Makefile2
-rw-r--r--usr.bin/netstat/inet6.c13
-rw-r--r--usr.bin/netstat/main.c2
-rw-r--r--usr.bin/netstat/route.c42
-rw-r--r--usr.sbin/gifconfig/Makefile21
-rw-r--r--usr.sbin/gifconfig/gifconfig.8140
-rw-r--r--usr.sbin/gifconfig/gifconfig.c834
-rw-r--r--usr.sbin/ifmcstat/Makefile14
-rw-r--r--usr.sbin/ifmcstat/ifmcstat.834
-rw-r--r--usr.sbin/ifmcstat/ifmcstat.c338
-rw-r--r--usr.sbin/prefix/Makefile12
-rw-r--r--usr.sbin/prefix/prefix.8217
-rw-r--r--usr.sbin/prefix/prefix.c592
-rw-r--r--usr.sbin/rip6query/Makefile9
-rw-r--r--usr.sbin/rip6query/rip6query.862
-rw-r--r--usr.sbin/rip6query/rip6query.c206
-rw-r--r--usr.sbin/route6d/Makefile9
-rwxr-xr-xusr.sbin/route6d/misc/chkrt64
-rw-r--r--usr.sbin/route6d/misc/cksum.c52
-rw-r--r--usr.sbin/route6d/route6d.8234
-rw-r--r--usr.sbin/route6d/route6d.c2913
-rw-r--r--usr.sbin/route6d/route6d.h91
-rw-r--r--usr.sbin/rtsold/Makefile25
-rw-r--r--usr.sbin/rtsold/dump.c137
-rw-r--r--usr.sbin/rtsold/if.c423
-rw-r--r--usr.sbin/rtsold/probe.c179
-rw-r--r--usr.sbin/rtsold/rtsol.c312
-rw-r--r--usr.sbin/rtsold/rtsold.8175
-rw-r--r--usr.sbin/rtsold/rtsold.c622
-rw-r--r--usr.sbin/rtsold/rtsold.h91
-rw-r--r--usr.sbin/traceroute6/Makefile26
-rw-r--r--usr.sbin/traceroute6/traceroute6.896
-rw-r--r--usr.sbin/traceroute6/traceroute6.c1127
49 files changed, 14758 insertions, 52 deletions
diff --git a/include/netdb.h b/include/netdb.h
index 987523e..4206c73 100644
--- a/include/netdb.h
+++ b/include/netdb.h
@@ -62,6 +62,7 @@
#define _NETDB_H_
#include <sys/cdefs.h>
+#include <sys/types.h>
#ifndef _PATH_HEQUIV
# define _PATH_HEQUIV "/etc/hosts.equiv"
@@ -111,6 +112,17 @@ struct protoent {
int p_proto; /* protocol # */
};
+struct addrinfo {
+ int ai_flags; /* AI_PASSIVE, AI_CANONNAME, AI_NUMERICHOST */
+ int ai_family; /* PF_xxx */
+ int ai_socktype; /* SOCK_xxx */
+ int ai_protocol; /* 0 or IPPROTO_xxx for IPv4 and IPv6 */
+ size_t ai_addrlen; /* length of ai_addr */
+ char *ai_canonname; /* canonical name for hostname */
+ struct sockaddr *ai_addr; /* binary address */
+ struct addrinfo *ai_next; /* next structure in linked list */
+};
+
/*
* Error return codes from gethostbyname() and gethostbyaddr()
* (left in extern int h_errno).
@@ -124,15 +136,74 @@ struct protoent {
#define NO_DATA 4 /* Valid name, no data record of requested type */
#define NO_ADDRESS NO_DATA /* no address, look for MX record */
+/*
+ * Error return codes from getaddrinfo()
+ */
+#define EAI_ADDRFAMILY 1 /* address family for hostname not supported */
+#define EAI_AGAIN 2 /* temporary failure in name resolution */
+#define EAI_BADFLAGS 3 /* invalid value for ai_flags */
+#define EAI_FAIL 4 /* non-recoverable failure in name resolution */
+#define EAI_FAMILY 5 /* ai_family not supported */
+#define EAI_MEMORY 6 /* memory allocation failure */
+#define EAI_NODATA 7 /* no address associated with hostname */
+#define EAI_NONAME 8 /* hostname nor servname provided, or not known */
+#define EAI_SERVICE 9 /* servname not supported for ai_socktype */
+#define EAI_SOCKTYPE 10 /* ai_socktype not supported */
+#define EAI_SYSTEM 11 /* system error returned in errno */
+#define EAI_BADHINTS 12
+#define EAI_PROTOCOL 13
+#define EAI_RESNULL 14
+#define EAI_MAX 15
+
+/*
+ * Flag values for getaddrinfo()
+ */
+#define AI_PASSIVE 0x00000001 /* get address to use bind() */
+#define AI_CANONNAME 0x00000002 /* fill ai_canonname */
+#define AI_NUMERICHOST 0x00000004 /* prevent name resolution */
+/* valid flags for addrinfo */
+#define AI_MASK (AI_PASSIVE | AI_CANONNAME | AI_NUMERICHOST)
+
+#define AI_ALL 0x00000100 /* IPv6 and IPv4-mapped (with AI_V4MAPPED) */
+#define AI_V4MAPPED_CFG 0x00000200 /* accept IPv4-mapped if kernel supports */
+#define AI_ADDRCONFIG 0x00000400 /* only if any address is assigned */
+#define AI_V4MAPPED 0x00000800 /* accept IPv4-mapped IPv6 address */
+/* special recommended flags for getipnodebyname */
+#define AI_DEFAULT (AI_V4MAPPED_CFG | AI_ADDRCONFIG)
+
+/*
+ * Constants for getnameinfo()
+ */
+#define NI_MAXHOST 1025
+#define NI_MAXSERV 32
+
+/*
+ * Flag values for getnameinfo()
+ */
+#define NI_NOFQDN 0x00000001
+#define NI_NUMERICHOST 0x00000002
+#define NI_NAMEREQD 0x00000004
+#define NI_NUMERICSERV 0x00000008
+#define NI_DGRAM 0x00000010
+#define NI_WITHSCOPEID 0x00000020
+
+/*
+ * Scope delimit character
+ */
+#define SCOPE_DELIMITER '@'
+
__BEGIN_DECLS
void endhostent __P((void));
void endnetent __P((void));
void endprotoent __P((void));
void endservent __P((void));
+void freehostent __P((struct hostent *));
struct hostent *gethostbyaddr __P((const char *, int, int));
struct hostent *gethostbyname __P((const char *));
struct hostent *gethostbyname2 __P((const char *, int));
struct hostent *gethostent __P((void));
+struct hostent *getipnodebyaddr __P((const void *, size_t, int, int *));
+struct hostent *getipnodebyname __P((const char *, int, int, int *));
struct netent *getnetbyaddr __P((unsigned long, int));
struct netent *getnetbyname __P((const char *));
struct netent *getnetent __P((void));
@@ -148,6 +219,12 @@ void sethostent __P((int));
/* void sethostfile __P((const char *)); */
void setnetent __P((int));
void setprotoent __P((int));
+int getaddrinfo __P((const char *, const char *,
+ const struct addrinfo *, struct addrinfo **));
+int getnameinfo __P((const struct sockaddr *, size_t, char *,
+ size_t, char *, size_t, int));
+void freeaddrinfo __P((struct addrinfo *));
+char *gai_strerror __P((int));
void setservent __P((int));
/*
diff --git a/include/resolv.h b/include/resolv.h
index e44e43a..4a0cf74 100644
--- a/include/resolv.h
+++ b/include/resolv.h
@@ -60,6 +60,7 @@
#include <sys/param.h>
#include <sys/types.h>
#include <sys/cdefs.h>
+#include <sys/socket.h>
#include <stdio.h>
/*
@@ -116,6 +117,21 @@ struct __res_state {
char pad[72]; /* on an i386 this means 512b total */
};
+/* for INET6 */
+/*
+ * replacement of __res_state, separated to keep binary compatibility.
+ */
+struct __res_state_ext {
+ struct sockaddr_storage nsaddr_list[MAXNS];
+ struct {
+ int af; /* address family for addr, mask */
+ union {
+ struct in_addr ina;
+ struct in6_addr in6a;
+ } addr, mask;
+ } sort_list[MAXRESOLVSORT];
+};
+
/*
* Resolver options (keep these in synch with res_debug.c, please)
*/
@@ -181,6 +197,9 @@ struct res_sym {
};
extern struct __res_state _res;
+/* for INET6 */
+extern struct __res_state_ext _res_ext;
+
extern const struct res_sym __p_class_syms[];
extern const struct res_sym __p_type_syms[];
diff --git a/lib/libc/net/Makefile.inc b/lib/libc/net/Makefile.inc
index 5b7dc85..87ac304 100644
--- a/lib/libc/net/Makefile.inc
+++ b/lib/libc/net/Makefile.inc
@@ -4,15 +4,16 @@
# machine-independent net sources
.PATH: ${.CURDIR}/../libc/${MACHINE_ARCH}/net ${.CURDIR}/../libc/net
-SRCS+= addr2ascii.c ascii2addr.c base64.c ether_addr.c \
+SRCS+= addr2ascii.c ascii2addr.c base64.c ether_addr.c getaddrinfo.c \
gethostbydns.c gethostbyht.c gethostbynis.c gethostnamadr.c \
+ getnameinfo.c \
getnetbydns.c getnetbyht.c getnetbynis.c getnetnamadr.c \
getproto.c getprotoent.c getprotoname.c getservbyname.c \
getservbyport.c getservent.c herror.c inet_addr.c ifname.c \
inet_lnaof.c \
inet_makeaddr.c inet_net_ntop.c inet_net_pton.c inet_neta.c \
inet_netof.c inet_network.c inet_ntoa.c inet_ntop.c \
- inet_pton.c ip6opt.c linkaddr.c map_v4v6.c ns_addr.c \
+ inet_pton.c ip6opt.c linkaddr.c map_v4v6.c name6.c ns_addr.c \
ns_name.c ns_netint.c \
ns_ntoa.c ns_parse.c ns_print.c ns_ttl.c nsap_addr.c \
rcmd.c recv.c res_comp.c res_data.c res_debug.c \
@@ -20,12 +21,15 @@ SRCS+= addr2ascii.c ascii2addr.c base64.c ether_addr.c \
res_update.c rthdr.c send.c vars.c
# not supported: iso_addr.c
+CFLAGS+=-DINET6
+
# machine-dependent net sources
.include "${.CURDIR}/../libc/${MACHINE_ARCH}/net/Makefile.inc"
.if ${LIB} == "c"
-MAN3+= addr2ascii.3 byteorder.3 ethers.3 gethostbyname.3 \
- getnetent.3 getprotoent.3 getservent.3 if_indextoname.3 \
+MAN3+= addr2ascii.3 byteorder.3 ethers.3 getaddrinfo.3 gethostbyname.3 \
+ getipnodebyname.3 \
+ getnameinfo.3 getnetent.3 getprotoent.3 getservent.3 if_indextoname.3 \
inet.3 linkaddr.3 rcmd.3 resolver.3
# not installed: iso_addr.3 ns.3
@@ -34,10 +38,12 @@ MLINKS+=byteorder.3 htonl.3 byteorder.3 htons.3 byteorder.3 ntohl.3 \
byteorder.3 ntohs.3
MLINKS+=ethers.3 ether_aton.3 ethers.3 ether_hostton.3 ethers.3 ether_line.3 \
ethers.3 ether_ntoa.3 ethers.3 ether_ntohost.3
+MLINKS+=getaddrinfo.3 freeaddrinfo.3 getaddrinfo.3 gai_strerror.3
MLINKS+=gethostbyname.3 endhostent.3 gethostbyname.3 gethostbyaddr.3 \
gethostbyname.3 gethostbyname2.3 gethostbyname.3 gethostent.3 \
gethostbyname.3 herror.3 gethostbyname.3 hstrerror.3 \
gethostbyname.3 sethostent.3
+MLINKS+=getipnodebyname.3 getipnodebyaddr.3 getipnodebyname.3 freehostent.3
MLINKS+=getnetent.3 endnetent.3 getnetent.3 getnetbyaddr.3 \
getnetent.3 getnetbyname.3 getnetent.3 setnetent.3
MLINKS+=getprotoent.3 endprotoent.3 getprotoent.3 getprotobyname.3 \
diff --git a/lib/libc/net/getaddrinfo.3 b/lib/libc/net/getaddrinfo.3
new file mode 100644
index 0000000..39bcdb7
--- /dev/null
+++ b/lib/libc/net/getaddrinfo.3
@@ -0,0 +1,408 @@
+.\" Copyright (c) 1983, 1987, 1991, 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.
+.\"
+.\" From: @(#)gethostbyname.3 8.4 (Berkeley) 5/25/95
+.\" $Id: getaddrinfo.3,v 1.3 1999/10/07 08:22:04 jinmei Exp $
+.\" $FreeBSD$
+.\"
+.Dd May 25, 1995
+.Dt GETADDRINFO 3
+.Os KAME
+.\"
+.Sh NAME
+.Nm getaddrinfo
+.Nm freeaddrinfo ,
+.Nm gai_strerror
+.Nd nodename-to-address translation in protocol-independent manner
+.\"
+.Sh SYNOPSIS
+.Fd #include <sys/socket.h>
+.Fd #include <netdb.h>
+.Ft int
+.Fn getaddrinfo "const char *nodename" "const char *servname" \
+"const struct addrinfo *hints" "struct addrinfo **res"
+.Ft void
+.Fn freeaddrinfo "struct addrinfo *ai"
+.Ft "char *"
+.Fn gai_strerror "int ecode"
+.\"
+.Sh DESCRIPTION
+The
+.Fn getaddrinfo
+function is defined for protocol-independent nodename-to-address translation.
+It performs functionality of
+.Xr gethostbyname 3
+and
+.Xr getservbyname 3 ,
+in more sophisticated manner.
+.Pp
+The addrinfo structure is defined as a result of including the
+.Li <netdb.h>
+header:
+.Bd -literal -offset
+struct addrinfo { *
+ int ai_flags; /* AI_PASSIVE, AI_CANONNAME, AI_NUMERICHOST */
+ int ai_family; /* PF_xxx */
+ int ai_socktype; /* SOCK_xxx */
+ int ai_protocol; /* 0 or IPPROTO_xxx for IPv4 and IPv6 */
+ size_t ai_addrlen; /* length of ai_addr */
+ char *ai_canonname; /* canonical name for nodename */
+ struct sockaddr *ai_addr; /* binary address */
+ struct addrinfo *ai_next; /* next structure in linked list */
+};
+.Ed
+.Pp
+The
+.Fa nodename
+and
+.Fa servname
+arguments are pointers to null-terminated strings or
+.Dv NULL .
+One or both of these two arguments must be a
+.Pf non Dv -NULL
+pointer.
+In the normal client scenario, both the
+.Fa nodename
+and
+.Fa servname
+are specified.
+In the normal server scenario, only the
+.Fa servname
+is specified.
+A
+.Pf non Dv -NULL
+.Fa nodename
+string can be either a node name or a numeric host address string
+.Po
+i.e., a dotted-decimal IPv4 address or an IPv6 hex address
+.Pc .
+A
+.Pf non Dv -NULL
+.Fa servname
+string can be either a service name or a decimal port number.
+.Pp
+The caller can optionally pass an
+.Li addrinfo
+structure, pointed to by the third argument,
+to provide hints concerning the type of socket that the caller supports.
+In this
+.Fa hints
+structure all members other than
+.Fa ai_flags ,
+.Fa ai_family ,
+.Fa ai_socktype ,
+and
+.Fa ai_protocol
+must be zero or a
+.Dv NULL
+pointer.
+A value of
+.Dv PF_UNSPEC
+for
+.Fa ai_family
+means the caller will accept any protocol family.
+A value of 0 for
+.Fa ai_socktype
+means the caller will accept any socket type.
+A value of 0 for
+.Fa ai_protocol
+means the caller will accept any protocol.
+For example, if the caller handles only TCP and not UDP, then the
+.Fa ai_socktype
+member of the hints structure should be set to
+.Dv SOCK_STREAM
+when
+.Fn getaddrinfo
+is called.
+If the caller handles only IPv4 and not IPv6, then the
+.Fa ai_family
+member of the
+.Fa hints
+structure should be set to
+.Dv PF_INET
+when
+.Fn getaddrinfo
+is called.
+If the third argument to
+.Fn getaddrinfo
+is a
+.Dv NULL
+pointer, this is the same as if the caller had filled in an
+.Li addrinfo
+structure initialized to zero with
+.Fa ai_family
+set to PF_UNSPEC.
+.Pp
+Upon successful return a pointer to a linked list of one or more
+.Li addrinfo
+structures is returned through the final argument.
+The caller can process each
+.Li addrinfo
+structure in this list by following the
+.Fa ai_next
+pointer, until a
+.Dv NULL
+pointer is encountered.
+In each returned
+.Li addrinfo
+structure the three members
+.Fa ai_family ,
+.Fa ai_socktype ,
+and
+.Fa ai_protocol
+are the corresponding arguments for a call to the
+.Fn socket
+function.
+In each
+.Li addrinfo
+structure the
+.Fa ai_addr
+member points to a filled-in socket address structure whose length is
+specified by the
+.Fa ai_addrlen
+member.
+.Pp
+If the
+.Dv AI_PASSIVE
+bit is set in the
+.Fa ai_flags
+member of the
+.Fa hints
+structure, then the caller plans to use the returned socket address
+structure in a call to
+.Fn bind .
+In this case, if the
+.Fa nodename
+argument is a
+.Dv NULL
+pointer, then the IP address portion of the socket
+address structure will be set to
+.Dv INADDR_ANY
+for an IPv4 address or
+.Dv IN6ADDR_ANY_INIT
+for an IPv6 address.
+.Pp
+If the
+.Dv AI_PASSIVE
+bit is not set in the
+.Fa ai_flags
+member of the
+.Fa hints
+structure, then the returned socket address structure will be ready for a
+call to
+.Fn connect
+.Pq for a connection-oriented protocol
+or either
+.Fn connect ,
+.Fn sendto , or
+.Fn sendmsg
+.Pq for a connectionless protocol .
+In this case, if the
+.Fa nodename
+argument is a
+.Dv NULL
+pointer, then the IP address portion of the
+socket address structure will be set to the loopback address.
+.Pp
+If the
+.Dv AI_CANONNAME
+bit is set in the
+.Fa ai_flags
+member of the
+.Fa hints
+structure, then upon successful return the
+.Fa ai_canonname
+member of the first
+.Li addrinfo
+structure in the linked list will point to a null-terminated string
+containing the canonical name of the specified
+.Fa nodename .
+.Pp
+If the
+.Dv AI_NUMERICHOST
+bit is set in the
+.Fa ai_flags
+member of the
+.Fa hints
+structure, then a
+.Pf non Dv -NULL
+.Fa nodename
+string must be a numeric host address string.
+Otherwise an error of
+.Dv EAI_NONAME
+is returned.
+This flag prevents any type of name resolution service (e.g., the DNS)
+from being called.
+.Pp
+All of the information returned by
+.Fn getaddrinfo
+is dynamically allocated:
+the
+.Li addrinfo
+structures, and the socket address structures and canonical node name
+strings pointed to by the addrinfo structures.
+To return this information to the system the function
+.Fn freeaddrinfo
+is called.
+The
+.Fa addrinfo
+structure pointed to by the
+.Fa ai argument
+is freed, along with any dynamic storage pointed to by the structure.
+This operation is repeated until a
+.Dv NULL
+.Fa ai_next
+pointer is encountered.
+.Pp
+To aid applications in printing error messages based on the
+.Dv EAI_xxx
+codes returned by
+.Fn getaddrinfo ,
+.Fn gai_strerror
+is defined.
+The argument is one of the
+.Dv EAI_xxx
+values defined earlier and the return value points to a string describing
+the error.
+If the argument is not one of the
+.Dv EAI_xxx
+values, the function still returns a pointer to a string whose contents
+indicate an unknown error.
+.\"
+.Sh EXTENSION
+The implementation allows experimental numeric IPv6 address notation with
+scope identifier.
+By appending atmark and scope identifier to addresses, you can fill
+.Li sin6_scope_id
+field for addresses.
+This would make management of scoped address easier,
+and allows cut-and-paste input of scoped address.
+.Pp
+At this moment the code supports only link-local addresses with the format.
+Scope identifier is hardcoded to name of hardware interface associated
+with the link.
+.Po
+such as
+.Li ne0
+.Pc .
+Example would be like
+.Dq Li fe80::1@ne0 ,
+which means
+.Do
+.Li fe80::1
+on the link associated with
+.Li ne0
+interface
+.Dc .
+.Pp
+The implementation is still very experimental and non-standard.
+The current implementation assumes one-by-one relationship between
+interface and link, which is not necessarily true from the specification.
+.\"
+.Sh FILES
+.Bl -tag -width /etc/resolv.conf -compact
+.It Pa /etc/hosts
+.It Pa /etc/host.conf
+.It Pa /etc/resolv.conf
+.El
+.\"
+.Sh DIAGNOSTICS
+Error return status from
+.Fn getaddrinfo
+is zero on success and non-zero on errors.
+Non-zero error codes are defined in
+.Li <netdb.h> ,
+and as follows:
+.Pp
+.Bl -tag -width EAI_ADDRFAMILY -compact
+.It Dv EAI_ADDRFAMILY
+address family for nodename not supported
+.It Dv EAI_AGAIN
+temporary failure in name resolution
+.It Dv EAI_BADFLAGS
+invalid value for ai_flags
+.It Dv EAI_FAIL
+non-recoverable failure in name resolution
+.It Dv EAI_FAMILY
+ai_family not supported
+.It Dv EAI_MEMORY
+memory allocation failure
+.It Dv EAI_NODATA
+no address associated with nodename
+.It Dv EAI_NONAME
+nodename nor servname provided, or not known
+.It Dv EAI_SERVICE
+servname not supported for ai_socktype
+.It Dv EAI_SOCKTYPE
+ai_socktype not supported
+.It Dv EAI_SYSTEM
+system error returned in errno
+.El
+.Pp
+If called with proper argument,
+.Fn gai_strerror
+returns a pointer to a string describing the given error code.
+If the argument is not one of the
+.Dv EAI_xxx
+values, the function still returns a pointer to a string whose contents
+indicate an unknown error.
+.\"
+.Sh SEE ALSO
+.Xr getnameinfo 3 ,
+.Xr gethostbyname 3 ,
+.Xr getservbyname 3 ,
+.Xr hosts 5 ,
+.Xr services 5 ,
+.Xr hostname 7 ,
+.Xr named 8 .
+.Pp
+.Rs
+.%A R. Gilligan
+.%A S. Thomson
+.%A J. Bound
+.%A W. Stevens
+.%T Basic Socket Interface Extensions for IPv6
+.%R RFC2553
+.%D March 1999
+.Re
+.\"
+.Sh HISTORY
+The implementation first appeared in WIDE Hydrangea IPv6 protocol stack kit.
+.\"
+.Sh STANDARDS
+The
+.Fn getaddrinfo
+function is defined IEEE POSIX 1003.1g draft specification,
+and documented in ``Basic Socket Interface Extensions for IPv6''
+.Pq RFC2533 .
+.\"
+.Sh BUGS
+The text was shamelessly copied from RFC2553.
diff --git a/lib/libc/net/getaddrinfo.c b/lib/libc/net/getaddrinfo.c
new file mode 100644
index 0000000..79b07de
--- /dev/null
+++ b/lib/libc/net/getaddrinfo.c
@@ -0,0 +1,1014 @@
+/*
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * "#ifdef FAITH" part is local hack for supporting IPv4-v6 translator.
+ *
+ * Issues to be discussed:
+ * - Thread safe-ness must be checked.
+ * - Return values. There are nonstandard return values defined and used
+ * in the source code. This is because RFC2553 is silent about which error
+ * code must be returned for which situation.
+ * - PF_UNSPEC case would be handled in getipnodebyname() with the AI_ALL flag.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <netdb.h>
+#include <resolv.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <stdio.h>
+
+#if defined(__KAME__) && defined(INET6)
+# define FAITH
+#endif
+
+#define SUCCESS 0
+#define ANY 0
+#define YES 1
+#define NO 0
+
+static const char in_addrany[] = { 0, 0, 0, 0 };
+static const char in6_addrany[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+static const char in_loopback[] = { 127, 0, 0, 1 };
+static const char in6_loopback[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
+};
+
+static const struct afd {
+ int a_af;
+ int a_addrlen;
+ int a_socklen;
+ int a_off;
+ const char *a_addrany;
+ const char *a_loopback;
+ int a_scoped;
+} afdl [] = {
+#ifdef INET6
+#define N_INET6 0
+ {PF_INET6, sizeof(struct in6_addr),
+ sizeof(struct sockaddr_in6),
+ offsetof(struct sockaddr_in6, sin6_addr),
+ in6_addrany, in6_loopback, 1},
+#define N_INET 1
+#else
+#define N_INET 0
+#endif
+ {PF_INET, sizeof(struct in_addr),
+ sizeof(struct sockaddr_in),
+ offsetof(struct sockaddr_in, sin_addr),
+ in_addrany, in_loopback, 0},
+ {0, 0, 0, 0, NULL, NULL, 0},
+};
+
+struct explore {
+ int e_af;
+ int e_socktype;
+ int e_protocol;
+ const char *e_protostr;
+ int e_wild;
+#define WILD_AF(ex) ((ex)->e_wild & 0x01)
+#define WILD_SOCKTYPE(ex) ((ex)->e_wild & 0x02)
+#define WILD_PROTOCOL(ex) ((ex)->e_wild & 0x04)
+};
+
+static const struct explore explore[] = {
+#ifdef INET6
+ { PF_INET6, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 },
+ { PF_INET6, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 },
+ { PF_INET6, SOCK_RAW, ANY, NULL, 0x05 },
+#endif
+ { PF_INET, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 },
+ { PF_INET, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 },
+ { PF_INET, SOCK_RAW, ANY, NULL, 0x05 },
+ { -1, 0, 0, NULL, 0 },
+};
+
+#ifdef INET6
+#define PTON_MAX 16
+#else
+#define PTON_MAX 4
+#endif
+
+
+static int str_isnumber __P((const char *));
+static int explore_fqdn __P((const struct addrinfo *, const char *,
+ const char *, struct addrinfo **));
+static int explore_null __P((const struct addrinfo *, const char *,
+ const char *, struct addrinfo **));
+static int explore_numeric __P((const struct addrinfo *, const char *,
+ const char *, struct addrinfo **));
+static int explore_numeric_scope __P((const struct addrinfo *, const char *,
+ const char *, struct addrinfo **));
+static int get_name __P((const char *, const struct afd *, struct addrinfo **,
+ char *, const struct addrinfo *, const char *));
+static int get_canonname __P((const struct addrinfo *,
+ struct addrinfo *, const char *));
+static struct addrinfo *get_ai __P((const struct addrinfo *,
+ const struct afd *, const char *));
+static int get_portmatch __P((const struct addrinfo *, const char *));
+static int get_port __P((struct addrinfo *, const char *, int));
+static const struct afd *find_afd __P((int));
+
+static char *ai_errlist[] = {
+ "Success",
+ "Address family for hostname not supported", /* EAI_ADDRFAMILY */
+ "Temporary failure in name resolution", /* EAI_AGAIN */
+ "Invalid value for ai_flags", /* EAI_BADFLAGS */
+ "Non-recoverable failure in name resolution", /* EAI_FAIL */
+ "ai_family not supported", /* EAI_FAMILY */
+ "Memory allocation failure", /* EAI_MEMORY */
+ "No address associated with hostname", /* EAI_NODATA */
+ "hostname nor servname provided, or not known", /* EAI_NONAME */
+ "servname not supported for ai_socktype", /* EAI_SERVICE */
+ "ai_socktype not supported", /* EAI_SOCKTYPE */
+ "System error returned in errno", /* EAI_SYSTEM */
+ "Invalid value for hints", /* EAI_BADHINTS */
+ "Resolved protocol is unknown", /* EAI_PROTOCOL */
+ "Argument res is NULL", /* EAI_RESNULL */
+ "Unknown error", /* EAI_MAX */
+};
+
+/* XXX macros that make external reference is BAD. */
+
+#define GET_AI(ai, afd, addr) \
+do { \
+ /* external reference: pai, error, and label free */ \
+ (ai) = get_ai(pai, (afd), (addr)); \
+ if ((ai) == NULL) { \
+ error = EAI_MEMORY; \
+ goto free; \
+ } \
+} while (0)
+
+#define GET_PORT(ai, serv) \
+do { \
+ /* external reference: error and label free */ \
+ error = get_port((ai), (serv), 0); \
+ if (error != 0) \
+ goto free; \
+} while (0)
+
+#define GET_CANONNAME(ai, str) \
+do { \
+ /* external reference: pai, error and label free */ \
+ error = get_canonname(pai, (ai), (str)); \
+ if (error != 0) \
+ goto free; \
+} while (0)
+
+#define ERR(err) \
+do { \
+ /* external reference: error, and label bad */ \
+ error = (err); \
+ goto bad; \
+} while (0)
+
+#define MATCH_FAMILY(x, y, w) \
+ ((x) == (y) || ((w) && ((x) == PF_UNSPEC || (y) == PF_UNSPEC)))
+#define MATCH(x, y, w) \
+ ((x) == (y) || ((w) && ((x) == ANY || (y) == ANY)))
+
+char *
+gai_strerror(ecode)
+ int ecode;
+{
+ if (ecode < 0 || ecode > EAI_MAX)
+ ecode = EAI_MAX;
+ return ai_errlist[ecode];
+}
+
+void
+freeaddrinfo(ai)
+ struct addrinfo *ai;
+{
+ struct addrinfo *next;
+
+ do {
+ next = ai->ai_next;
+ if (ai->ai_canonname)
+ free(ai->ai_canonname);
+ /* no need to free(ai->ai_addr) */
+ free(ai);
+ } while ((ai = next) != NULL);
+}
+
+static int
+str_isnumber(p)
+ const char *p;
+{
+ char *q = (char *)p;
+ while (*q) {
+ if (! isdigit(*q))
+ return NO;
+ q++;
+ }
+ return YES;
+}
+
+int
+getaddrinfo(hostname, servname, hints, res)
+ const char *hostname, *servname;
+ const struct addrinfo *hints;
+ struct addrinfo **res;
+{
+ struct addrinfo sentinel;
+ struct addrinfo *cur;
+ int error = 0;
+ struct addrinfo ai;
+ struct addrinfo ai0;
+ struct addrinfo *pai;
+ const struct afd *afd;
+ const struct explore *ex;
+
+ sentinel.ai_next = NULL;
+ cur = &sentinel;
+ pai = &ai;
+ pai->ai_flags = 0;
+ pai->ai_family = PF_UNSPEC;
+ pai->ai_socktype = ANY;
+ pai->ai_protocol = ANY;
+ pai->ai_addrlen = 0;
+ pai->ai_canonname = NULL;
+ pai->ai_addr = NULL;
+ pai->ai_next = NULL;
+
+ if (hostname == NULL && servname == NULL)
+ return EAI_NONAME;
+ if (res == NULL)
+ return EAI_RESNULL; /* xxx */
+ if (hints) {
+ /* error check for hints */
+ if (hints->ai_addrlen || hints->ai_canonname ||
+ hints->ai_addr || hints->ai_next)
+ ERR(EAI_BADHINTS); /* xxx */
+ if (hints->ai_flags & ~AI_MASK)
+ ERR(EAI_BADFLAGS);
+ switch (hints->ai_family) {
+ case PF_UNSPEC:
+ case PF_INET:
+#ifdef INET6
+ case PF_INET6:
+#endif
+ break;
+ default:
+ ERR(EAI_FAMILY);
+ }
+ memcpy(pai, hints, sizeof(*pai));
+
+ /*
+ * if both socktype/protocol are specified, check if they
+ * are meaningful combination.
+ */
+ if (pai->ai_socktype != ANY && pai->ai_protocol != ANY) {
+ int matched = 0;
+
+ for (ex = explore; ex->e_af >= 0; ex++) {
+ if (pai->ai_family != ex->e_af)
+ continue;
+ if (ex->e_socktype == ANY)
+ continue;
+ if (ex->e_protocol == ANY)
+ continue;
+ if (pai->ai_socktype == ex->e_socktype
+ && pai->ai_protocol == ex->e_protocol)
+ matched = 1;
+ else
+ continue;
+ if (matched == 0)
+ ERR(EAI_BADHINTS);
+ }
+ }
+ }
+
+ /* backup original pai contents */
+ ai0 = *pai;
+
+ /*
+ * special cases check for inet and inet6 sockets.
+ * (1) servname is disallowed for raw sockets.
+ * (2) numeric servname is disallowed if socktype/protocol is left
+ * unspecified.
+ */
+ if (MATCH_FAMILY(pai->ai_family, PF_INET, 1)
+#ifdef INET6
+ || MATCH_FAMILY(pai->ai_family, PF_INET6, 1)
+#endif
+ ) {
+ *pai = ai0;
+
+ if (pai->ai_family == PF_UNSPEC)
+#ifdef INET6
+ pai->ai_family = PF_INET6;
+#else
+ pai->ai_family = PF_INET;
+#endif
+ error = get_portmatch(pai, servname);
+ if (error)
+ ERR(error);
+ }
+
+ /* NULL hostname, or numeric hostname */
+ for (ex = explore; ex->e_af >= 0; ex++) {
+ *pai = ai0;
+
+ if (!MATCH_FAMILY(pai->ai_family, ex->e_af, WILD_AF(ex)))
+ continue;
+ if (!MATCH(pai->ai_socktype, ex->e_socktype, WILD_SOCKTYPE(ex)))
+ continue;
+ if (!MATCH(pai->ai_protocol, ex->e_protocol, WILD_PROTOCOL(ex)))
+ continue;
+
+ if (pai->ai_family == PF_UNSPEC)
+ pai->ai_family = ex->e_af;
+ if (pai->ai_socktype == ANY && ex->e_socktype != ANY)
+ pai->ai_socktype = ex->e_socktype;
+ if (pai->ai_protocol == ANY && ex->e_protocol != ANY)
+ pai->ai_protocol = ex->e_protocol;
+
+ if (hostname == NULL)
+ error = explore_null(pai, hostname, servname, &cur->ai_next);
+ else
+ error = explore_numeric_scope(pai, hostname, servname, &cur->ai_next);
+
+ if (error)
+ goto free;
+
+ while (cur && cur->ai_next)
+ cur = cur->ai_next;
+ }
+
+ /*
+ * XXX
+ * If numreic representation of AF1 can be interpreted as FQDN
+ * representation of AF2, we need to think again about the code below.
+ */
+ if (sentinel.ai_next)
+ goto good;
+
+ if (pai->ai_flags & AI_NUMERICHOST)
+ ERR(EAI_NONAME);
+ if (hostname == NULL)
+ ERR(EAI_NONAME);
+
+ /*
+ * hostname as alphabetical name.
+ * we would like to prefer AF_INET6 than AF_INET, so we'll make a
+ * outer loop by AFs.
+ */
+ for (afd = afdl; afd->a_af; afd++) {
+ *pai = ai0;
+
+ if (!MATCH_FAMILY(pai->ai_family, afd->a_af, 1))
+ continue;
+
+ for (ex = explore; ex->e_af >= 0; ex++) {
+ *pai = ai0;
+
+ if (pai->ai_family == PF_UNSPEC)
+ pai->ai_family = afd->a_af;
+
+ if (!MATCH_FAMILY(pai->ai_family, ex->e_af,
+ WILD_AF(ex)))
+ continue;
+ if (!MATCH(pai->ai_socktype, ex->e_socktype,
+ WILD_SOCKTYPE(ex))) {
+ continue;
+ }
+ if (!MATCH(pai->ai_protocol, ex->e_protocol,
+ WILD_PROTOCOL(ex))) {
+ continue;
+ }
+
+ if (pai->ai_family == PF_UNSPEC)
+ pai->ai_family = ex->e_af;
+ if (pai->ai_socktype == ANY && ex->e_socktype != ANY)
+ pai->ai_socktype = ex->e_socktype;
+ if (pai->ai_protocol == ANY && ex->e_protocol != ANY)
+ pai->ai_protocol = ex->e_protocol;
+
+ error = explore_fqdn(pai, hostname, servname,
+ &cur->ai_next);
+
+ while (cur && cur->ai_next)
+ cur = cur->ai_next;
+ }
+ }
+
+ /* XXX: if any addrinfo found, SUCCESS return even if (error != 0) */
+ if (sentinel.ai_next) {
+ good:
+ *res = sentinel.ai_next;
+ return SUCCESS;
+ }
+ /* else, failed */
+ free:
+ bad:
+ if (error == 0)
+ error = EAI_FAIL;
+ if (sentinel.ai_next)
+ freeaddrinfo(sentinel.ai_next);
+ *res = NULL;
+ return error;
+}
+
+/*
+ * FQDN hostname, DNS lookup
+ */
+static int
+explore_fqdn(pai, hostname, servname, res)
+ const struct addrinfo *pai;
+ const char *hostname;
+ const char *servname;
+ struct addrinfo **res;
+{
+ int s;
+ struct hostent *hp;
+ int h_error;
+ int af;
+ char *ap;
+ struct addrinfo sentinel, *cur;
+ int i;
+ const struct afd *afd;
+ int error;
+
+ *res = NULL;
+ sentinel.ai_next = NULL;
+ cur = &sentinel;
+
+ /*
+ * filter out AFs that are not supported by the kernel
+ * XXX errno?
+ */
+ s = socket(pai->ai_family, SOCK_DGRAM, 0);
+ if (s < 0)
+ return 0;
+ close(s);
+
+ /*
+ * if the servname does not match socktype/protocol, ignore it.
+ */
+ if (get_portmatch(pai, servname) != 0)
+ return 0;
+
+ afd = find_afd(pai->ai_family);
+ if (afd == NULL)
+ return 0;
+
+ hp = getipnodebyname(hostname, pai->ai_family, AI_ADDRCONFIG,
+ &h_error);
+ if (hp == NULL) {
+ switch (h_error) {
+ case HOST_NOT_FOUND:
+ case NO_DATA:
+ error = EAI_NODATA;
+ break;
+ case TRY_AGAIN:
+ error = EAI_AGAIN;
+ break;
+ case NO_RECOVERY:
+ case NETDB_INTERNAL:
+ default:
+ error = EAI_FAIL;
+ break;
+ }
+ } else if ((hp->h_name == NULL) || (hp->h_name[0] == 0)
+ || (hp->h_addr_list[0] == NULL)) {
+ freehostent(hp);
+ hp = NULL;
+ error = EAI_FAIL;
+ }
+
+ if (hp == NULL)
+ goto free;
+
+ for (i = 0; hp->h_addr_list[i] != NULL; i++) {
+ af = hp->h_addrtype;
+ ap = hp->h_addr_list[i];
+
+ if (af != pai->ai_family)
+ continue;
+
+ if ((pai->ai_flags & AI_CANONNAME) == 0) {
+ GET_AI(cur->ai_next, afd, ap);
+ GET_PORT(cur->ai_next, servname);
+ } else {
+ /*
+ * if AI_CANONNAME and if reverse lookup
+ * fail, return ai anyway to pacify
+ * calling application.
+ *
+ * XXX getaddrinfo() is a name->address
+ * translation function, and it looks
+ * strange that we do addr->name
+ * translation here.
+ */
+ get_name(ap, afd, &cur->ai_next,
+ ap, pai, servname);
+ }
+
+ while (cur && cur->ai_next)
+ cur = cur->ai_next;
+ }
+
+ *res = sentinel.ai_next;
+ return 0;
+
+free:
+ if (hp)
+ freehostent(hp);
+ if (sentinel.ai_next)
+ freeaddrinfo(sentinel.ai_next);
+ return error;
+}
+
+/*
+ * hostname == NULL.
+ * passive socket -> anyaddr (0.0.0.0 or ::)
+ * non-passive socket -> localhost (127.0.0.1 or ::1)
+ */
+static int
+explore_null(pai, hostname, servname, res)
+ const struct addrinfo *pai;
+ const char *hostname;
+ const char *servname;
+ struct addrinfo **res;
+{
+ int s;
+ const struct afd *afd;
+ struct addrinfo *cur;
+ struct addrinfo sentinel;
+ int error;
+
+ *res = NULL;
+ sentinel.ai_next = NULL;
+ cur = &sentinel;
+
+ /*
+ * filter out AFs that are not supported by the kernel
+ * XXX errno?
+ */
+ s = socket(pai->ai_family, SOCK_DGRAM, 0);
+ if (s < 0)
+ return 0;
+ close(s);
+ afd = find_afd(pai->ai_family);
+ if (afd == NULL)
+ return 0;
+
+ GET_AI(cur->ai_next, afd,
+ (pai->ai_flags & AI_PASSIVE) ? afd->a_addrany : afd->a_loopback
+ );
+ /* xxx meaningless?
+ * GET_CANONNAME(cur->ai_next, "anyaddr");
+ * or
+ * GET_CANONNAME(cur->ai_next, "localhost");
+ */
+ /* if the servname does not match socktype/protocol, ignored */
+ GET_PORT(cur->ai_next, servname);
+
+ *res = sentinel.ai_next;
+ return 0;
+
+free:
+ if (sentinel.ai_next)
+ freeaddrinfo(sentinel.ai_next);
+ return error;
+}
+
+/*
+ * numeric hostname
+ */
+static int
+explore_numeric(pai, hostname, servname, res)
+ const struct addrinfo *pai;
+ const char *hostname;
+ const char *servname;
+ struct addrinfo **res;
+{
+ const struct afd *afd;
+ struct addrinfo *cur;
+ struct addrinfo sentinel;
+ int error;
+ char pton[PTON_MAX];
+ int flags;
+
+ *res = NULL;
+ sentinel.ai_next = NULL;
+ cur = &sentinel;
+
+ /*
+ * if the servname does not match socktype/protocol, ignore it.
+ */
+ if (get_portmatch(pai, servname) != 0)
+ return 0;
+
+ afd = find_afd(pai->ai_family);
+ if (afd == NULL)
+ return 0;
+ flags = pai->ai_flags;
+
+ if (inet_pton(afd->a_af, hostname, pton) == 1) {
+ u_int32_t v4a;
+#ifdef INET6
+ struct in6_addr * v6a;
+#endif
+
+ switch (afd->a_af) {
+ case AF_INET:
+ v4a = (u_int32_t)ntohl(((struct in_addr *)pton)->s_addr);
+ if (IN_MULTICAST(v4a) || IN_EXPERIMENTAL(v4a))
+ flags &= ~AI_CANONNAME;
+ v4a >>= IN_CLASSA_NSHIFT;
+ if (v4a == 0 || v4a == IN_LOOPBACKNET)
+ flags &= ~AI_CANONNAME;
+ break;
+#ifdef INET6
+ case AF_INET6:
+ v6a = (struct in6_addr *)pton;
+ if (IN6_IS_ADDR_MULTICAST(v6a))
+ flags &= ~AI_CANONNAME;
+ if (IN6_IS_ADDR_UNSPECIFIED(v6a) ||
+ IN6_IS_ADDR_LOOPBACK(v6a))
+ flags &= ~AI_CANONNAME;
+ if (IN6_IS_ADDR_LINKLOCAL(v6a))
+ flags &= ~AI_CANONNAME;
+
+ /* should also do this for SITELOCAL ?? */
+
+ break;
+#endif
+ }
+
+ if (pai->ai_family == afd->a_af ||
+ pai->ai_family == PF_UNSPEC /*?*/) {
+ if ((flags & AI_CANONNAME) == 0) {
+ GET_AI(cur->ai_next, afd, pton);
+ GET_PORT(cur->ai_next, servname);
+ } else {
+ /*
+ * if AI_CANONNAME and if reverse lookup
+ * fail, return ai anyway to pacify
+ * calling application.
+ *
+ * XXX getaddrinfo() is a name->address
+ * translation function, and it looks
+ * strange that we do addr->name
+ * translation here.
+ */
+ get_name(pton, afd, &cur->ai_next,
+ pton, pai, servname);
+ }
+ while (cur && cur->ai_next)
+ cur = cur->ai_next;
+ } else
+ ERR(EAI_FAMILY); /*xxx*/
+ }
+
+ *res = sentinel.ai_next;
+ return 0;
+
+free:
+bad:
+ if (sentinel.ai_next)
+ freeaddrinfo(sentinel.ai_next);
+ return error;
+}
+
+/*
+ * numeric hostname with scope
+ */
+static int
+explore_numeric_scope(pai, hostname, servname, res)
+ const struct addrinfo *pai;
+ const char *hostname;
+ const char *servname;
+ struct addrinfo **res;
+{
+#ifndef SCOPE_DELIMITER
+ return explore_numeric(pai, hostname, servname, res);
+#else
+ const struct afd *afd;
+ struct addrinfo *cur;
+ int error;
+ char *cp, *hostname2 = NULL;
+ int scope;
+#ifdef INET6
+ struct sockaddr_in6 *sin6;
+#endif
+
+ /*
+ * if the servname does not match socktype/protocol, ignore it.
+ */
+ if (get_portmatch(pai, servname) != 0)
+ return 0;
+
+ afd = find_afd(pai->ai_family);
+ if (afd == NULL)
+ return 0;
+ if (!afd->a_scoped)
+ return explore_numeric(pai, hostname, servname, res);
+
+ cp = strchr(hostname, SCOPE_DELIMITER);
+ if (cp == NULL)
+ return explore_numeric(pai, hostname, servname, res);
+
+ /*
+ * Handle special case of <scoped_address><delimiter><scope id>
+ */
+ hostname2 = strdup(hostname);
+ if (hostname2 == NULL)
+ return EAI_MEMORY;
+ /* terminate at the delimiter */
+ hostname2[cp - hostname] = '\0';
+
+ cp++;
+ switch (pai->ai_family) {
+#ifdef INET6
+ case AF_INET6:
+ scope = if_nametoindex(cp);
+ break;
+#endif
+ }
+
+ error = explore_numeric(pai, hostname2, servname, res);
+ if (error == 0) {
+ for (cur = *res; cur; cur = cur->ai_next) {
+#ifdef INET6
+ if (cur->ai_family != AF_INET6)
+ continue;
+ sin6 = (struct sockaddr_in6 *)cur->ai_addr;
+ if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) ||
+ IN6_IS_ADDR_MC_LINKLOCAL(&sin6->sin6_addr))
+ sin6->sin6_scope_id = scope;
+#endif
+ }
+ }
+
+ free(hostname2);
+
+ return error;
+#endif
+}
+
+static int
+get_name(addr, afd, res, numaddr, pai, servname)
+ const char *addr;
+ const struct afd *afd;
+ struct addrinfo **res;
+ char *numaddr;
+ const struct addrinfo *pai;
+ const char *servname;
+{
+ struct hostent *hp;
+ struct addrinfo *cur;
+ int error = 0;
+ int h_error;
+
+ hp = getipnodebyaddr(addr, afd->a_addrlen, afd->a_af, &h_error);
+ if (hp && hp->h_name && hp->h_name[0] && hp->h_addr_list[0]) {
+ GET_AI(cur, afd, hp->h_addr_list[0]);
+ GET_PORT(cur, servname);
+ GET_CANONNAME(cur, hp->h_name);
+ } else {
+ GET_AI(cur, afd, numaddr);
+ GET_PORT(cur, servname);
+ }
+
+ if (hp)
+ freehostent(hp);
+ *res = cur;
+ return SUCCESS;
+ free:
+ if (cur)
+ freeaddrinfo(cur);
+ if (hp)
+ freehostent(hp);
+ /* bad: */
+ *res = NULL;
+ return error;
+}
+
+static int
+get_canonname(pai, ai, str)
+ const struct addrinfo *pai;
+ struct addrinfo *ai;
+ const char *str;
+{
+ if ((pai->ai_flags & AI_CANONNAME) != 0) {
+ ai->ai_canonname = (char *)malloc(strlen(str) + 1);
+ if (ai->ai_canonname == NULL)
+ return EAI_MEMORY;
+ strcpy(ai->ai_canonname, str);
+ }
+ return 0;
+}
+
+static struct addrinfo *
+get_ai(pai, afd, addr)
+ const struct addrinfo *pai;
+ const struct afd *afd;
+ const char *addr;
+{
+ char *p;
+ struct addrinfo *ai;
+#ifdef FAITH
+ struct in6_addr faith_prefix;
+ char *fp_str;
+ int translate = 0;
+#endif
+
+#ifdef FAITH
+ /*
+ * Transfrom an IPv4 addr into a special IPv6 addr format for
+ * IPv6->IPv4 translation gateway. (only TCP is supported now)
+ *
+ * +-----------------------------------+------------+
+ * | faith prefix part (12 bytes) | embedded |
+ * | | IPv4 addr part (4 bytes)
+ * +-----------------------------------+------------+
+ *
+ * faith prefix part is specified as ascii IPv6 addr format
+ * in environmental variable GAI.
+ * For FAITH to work correctly, routing to faith prefix must be
+ * setup toward a machine where a FAITH daemon operates.
+ * Also, the machine must enable some mechanizm
+ * (e.g. faith interface hack) to divert those packet with
+ * faith prefixed destination addr to user-land FAITH daemon.
+ */
+ fp_str = getenv("GAI");
+ if (fp_str && inet_pton(AF_INET6, fp_str, &faith_prefix) == 1 &&
+ afd->a_af == AF_INET && pai->ai_socktype == SOCK_STREAM) {
+ u_int32_t v4a;
+ u_int8_t v4a_top;
+
+ memcpy(&v4a, addr, sizeof v4a);
+ v4a_top = v4a >> IN_CLASSA_NSHIFT;
+ if (!IN_MULTICAST(v4a) && !IN_EXPERIMENTAL(v4a) &&
+ v4a_top != 0 && v4a != IN_LOOPBACKNET) {
+ afd = &afdl[N_INET6];
+ memcpy(&faith_prefix.s6_addr[12], addr,
+ sizeof(struct in_addr));
+ translate = 1;
+ }
+ }
+#endif
+
+ ai = (struct addrinfo *)malloc(sizeof(struct addrinfo)
+ + (afd->a_socklen));
+ if (ai == NULL)
+ return NULL;
+
+ memcpy(ai, pai, sizeof(struct addrinfo));
+ ai->ai_addr = (struct sockaddr *)(ai + 1);
+ memset(ai->ai_addr, 0, afd->a_socklen);
+ ai->ai_addr->sa_len = afd->a_socklen;
+ ai->ai_addrlen = afd->a_socklen;
+ ai->ai_addr->sa_family = ai->ai_family = afd->a_af;
+ p = (char *)(ai->ai_addr);
+#ifdef FAITH
+ if (translate == 1)
+ memcpy(p + afd->a_off, &faith_prefix, afd->a_addrlen);
+ else
+#endif
+ memcpy(p + afd->a_off, addr, afd->a_addrlen);
+
+ return ai;
+}
+
+static int
+get_portmatch(ai, servname)
+ const struct addrinfo *ai;
+ const char *servname;
+{
+ /* get_port does not touch first argument. when matchonly == 1. */
+ return get_port((struct addrinfo *)ai, servname, 1);
+}
+
+static int
+get_port(ai, servname, matchonly)
+ struct addrinfo *ai;
+ const char *servname;
+ int matchonly;
+{
+ const char *proto;
+ struct servent *sp;
+ int port;
+ int allownumeric;
+
+ if (servname == NULL)
+ return 0;
+ if (ai->ai_family != AF_INET
+#ifdef INET6
+ && ai->ai_family != AF_INET6
+#endif
+ )
+ return 0;
+
+ switch (ai->ai_socktype) {
+ case SOCK_RAW:
+ return EAI_SERVICE;
+ case SOCK_DGRAM:
+ case SOCK_STREAM:
+ allownumeric = 1;
+ break;
+ case ANY:
+ allownumeric = 0;
+ break;
+ default:
+ return EAI_SOCKTYPE;
+ }
+
+ if (str_isnumber(servname)) {
+ if (!allownumeric)
+ return EAI_SERVICE;
+ port = htons(atoi(servname));
+ if (port < 0 || port > 65535)
+ return EAI_SERVICE;
+ } else {
+ switch (ai->ai_socktype) {
+ case SOCK_DGRAM:
+ proto = "udp";
+ break;
+ case SOCK_STREAM:
+ proto = "tcp";
+ break;
+ default:
+ proto = NULL;
+ break;
+ }
+
+ if ((sp = getservbyname(servname, proto)) == NULL)
+ return EAI_SERVICE;
+ port = sp->s_port;
+ }
+
+ if (!matchonly) {
+ switch (ai->ai_family) {
+ case AF_INET:
+ ((struct sockaddr_in *)ai->ai_addr)->sin_port = port;
+ break;
+#ifdef INET6
+ case AF_INET6:
+ ((struct sockaddr_in6 *)ai->ai_addr)->sin6_port = port;
+ break;
+#endif
+ }
+ }
+
+ return 0;
+}
+
+static const struct afd *
+find_afd(af)
+ int af;
+{
+ const struct afd *afd;
+
+ if (af == PF_UNSPEC)
+ return NULL;
+ for (afd = afdl; afd->a_af; afd++) {
+ if (afd->a_af == af)
+ return afd;
+ }
+ return NULL;
+}
diff --git a/lib/libc/net/getipnodebyname.3 b/lib/libc/net/getipnodebyname.3
new file mode 100644
index 0000000..7220af4
--- /dev/null
+++ b/lib/libc/net/getipnodebyname.3
@@ -0,0 +1,446 @@
+.\" Copyright (c) 1983, 1987, 1991, 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.
+.\"
+.\" From: @(#)gethostbyname.3 8.4 (Berkeley) 5/25/95
+.\" $Id: getipnodebyname.3,v 1.2 1999/09/13 16:04:51 itojun Exp $
+.\ $FreeBSD$
+.\"
+.Dd May 25, 1995
+.Dt GETIPNODEBYNAME 3
+.Os KAME
+.\"
+.Sh NAME
+.Nm getipnodebyname ,
+.Nm getipnodebyaddr ,
+.Nm freehostent
+.Nd nodename-to-address and address-to-nodename translation
+.\"
+.Sh SYNOPSIS
+.Fd #include <sys/socket.h>
+.Fd #include <netdb.h>
+.Ft "struct hostent *"
+.Fn getipnodebyname "const char *name" "int af" "int flags" "int *error_num"
+.Ft "struct hostent *"
+.Fn getipnodebyaddr "const void *src" "size_t len" "int af" "int *error_num"
+.Ft void
+.Fn freehostent "struct hostent *ptr"
+.\"
+.Sh DESCRIPTION
+.Fn getipnodebyname
+and
+.Fn getipnodebyaddr
+functions are very similar to
+.Xr gethostbyname 3 ,
+.Xr gethostbyname2 3
+and
+.Xr gethostbyaddr 3 .
+The functions cover all the functionalities provided by the older ones,
+and provide better interface to programmers.
+The functions require additional arguments,
+.Ar af ,
+and
+.Ar flags ,
+for specifying address family and operation mode.
+The additional arguments allow programmer to get address for a nodename,
+for specific address family
+.Po
+such as
+.Dv AF_INET
+or
+.Dv AF_INET6
+.Pc .
+The functions also require an additional pointer argument,
+.Ar error_num
+to return the appropriate error code,
+to support thread safe error code returns.
+.Pp
+The type and usage of the return value,
+.Li "struct hostent"
+is described in
+.Xr gethostbyname 3 .
+.Pp
+For
+.Fn getipnodebyname ,
+the
+.Ar name
+argument can be either a node name or a numeric address
+string
+.Po
+i.e., a dotted-decimal IPv4 address or an IPv6 hex address
+.Pc .
+The
+.Ar af
+argument specifies the address family, either
+.Dv AF_INET
+or
+.Dv AF_INET6 .
+The
+.Ar flags
+argument specifies the types of addresses that are searched for,
+and the types of addresses that are returned.
+We note that a special flags value of
+.Dv AI_DEFAULT
+.Pq defined below
+should handle most applications.
+That is, porting simple applications to use IPv6 replaces the call
+.Bd -literal -offset
+ hptr = gethostbyname(name);
+.Ed
+.Pp
+with
+.Bd -literal -offset
+ hptr = getipnodebyname(name, AF_INET6, AI_DEFAULT, &error_num);
+.Ed
+.Pp
+Applications desiring finer control over the types of addresses
+searched for and returned, can specify other combinations of the
+.Ar flags
+argument.
+.Pp
+A
+.Ar flags
+of
+.Li 0
+implies a strict interpretation of the
+.Ar af
+argument:
+.Bl -bullet
+.It
+If
+.Ar flags
+is 0 and
+.Ar af
+is
+.Dv AF_INET ,
+then the caller wants only IPv4 addresses.
+A query is made for
+.Li A
+records.
+If successful, the IPv4 addresses are returned and the
+.Li h_length
+member of the
+.Li hostent
+structure will be 4, else the function returns a
+.Dv NULL
+pointer.
+.It
+If
+.Ar flags
+is 0 and if
+.Ar af
+is
+.Li AF_INET6 ,
+then the caller wants only IPv6 addresses.
+A query is made for
+.Li AAAA
+records.
+If successful, the IPv6 addresses are returned and the
+.Li h_length
+member of the
+.Li hostent
+structure will be 16, else the function returns a
+.Dv NULL
+pointer.
+.El
+.Pp
+Other constants can be logically-ORed into the
+.Ar flags
+argument, to modify the behavior of the function.
+.Bl -bullet
+.It
+If the
+.Dv AI_V4MAPPED
+flag is specified along with an
+.Ar af
+of
+.Dv AF_INET6 ,
+then the caller will accept IPv4-mapped IPv6 addresses.
+That is, if no
+.Li AAAA
+records are found then a query is made for
+.Li A
+records and any found are returned as IPv4-mapped IPv6 addresses
+.Po
+.Li h_length
+will be 16
+.Pc .
+The
+.Dv AI_V4MAPPED
+flag is ignored unless
+.Ar af
+equals
+.Dv AF_INET6 .
+.It
+The
+.Dv AI_V4MAPPED_CFG
+flag is exact same as the
+.Dv AI_V4MAPPED
+flag only if the kernel supports IPv4-mapped IPv6 address.
+.It
+If the
+.Dv AI_ALL
+flag is used in conjunction with the
+.Dv AI_V4MAPPED
+flag, and only used with the IPv6 address family.
+When
+.Dv AI_ALL
+is logically or'd with
+.Dv AI_V4MAPPED
+flag then the caller wants all addresses: IPv6 and IPv4-mapped IPv6.
+A query is first made for
+.Li AAAA
+records and if successful, the
+IPv6 addresses are returned. Another query is then made for
+.Li A
+records and any found are returned as IPv4-mapped IPv6 addresses.
+.Li h_length
+will be 16. Only if both queries fail does the function
+return a
+.Dv NULL
+pointer. This flag is ignored unless af equals
+AF_INET6. If both
+.Dv AI_ALL
+and
+.Dv AI_V4MAPPED
+are specified,
+.Dv AI_ALL
+takes precedence.
+.It
+The
+.Dv AI_ADDRCONFIG
+flag specifies that a query for
+.Li AAAA
+records
+should occur only if the node has at least one IPv6 source
+address configured and a query for
+.Li A
+records should occur only if the node has at least one IPv4 source address
+configured.
+.Pp
+For example, if the node has no IPv6 source addresses configured,
+and
+.Ar af
+equals AF_INET6, and the node name being looked up has both
+.Li AAAA
+and
+.Li A
+records, then:
+(a) if only
+.Dv AI_ADDRCONFIG
+is
+specified, the function returns a
+.Dv NULL
+pointer;
+(b) if
+.Dv AI_ADDRCONFIG
+|
+.Dv AI_V4MAPPED
+is specified, the
+.Li A
+records are returned as IPv4-mapped IPv6 addresses;
+.El
+.Pp
+The special flags value of
+.Dv AI_DEFAULT
+is defined as
+.Bd -literal -offset
+ #define AI_DEFAULT (AI_V4MAPPED_CFG | AI_ADDRCONFIG)
+.Ed
+.Pp
+We noted that the
+.Fn getipnodebyname
+function must allow the
+.Ar name
+argument to be either a node name or a literal address string
+.Po
+i.e., a dotted-decimal IPv4 address or an IPv6 hex address
+.Pc .
+This saves applications from having to call
+.Xr inet_pton 3
+to handle literal address strings.
+When the
+.Ar name
+argument is a literal address string,
+the
+.Ar flags
+argument is always ignored.
+.Pp
+There are four scenarios based on the type of literal address string
+and the value of the
+.Ar af
+argument.
+The two simple cases are when
+.Ar name
+is a dotted-decimal IPv4 address and
+.Ar af
+equals
+.Dv AF_INET ,
+or when
+.Ar name
+is an IPv6 hex address and
+.Ar af
+equals
+.Dv AF_INET6 .
+The members of the
+returned hostent structure are:
+.Li h_name
+points to a copy of the
+.Ar name
+argument,
+.Li h_aliases
+is a
+.Dv NULL
+pointer,
+.Li h_addrtype
+is a copy of the
+.Ar af
+argument,
+.Li h_length
+is either 4
+.Po
+for
+.Dv AF_INET
+.Pc
+or 16
+.Po
+for
+.Dv AF_INET6
+.Pc ,
+.Li h_addr_list[0]
+is a pointer to the 4-byte or 16-byte binary address,
+and
+.Li h_addr_list[1]
+is a
+.Dv NULL
+pointer.
+.Pp
+When
+.Ar name
+is a dotted-decimal IPv4 address and
+.Ar af
+equals
+.Dv AF_INET6 ,
+and
+.Dv AI_V4MAPPED
+is specified,
+an IPv4-mapped IPv6 address is returned:
+.Li h_name
+points to an IPv6 hex address containing the IPv4-mapped IPv6 address,
+.Li h_aliases
+is a
+.Dv NULL
+pointer,
+.Li h_addrtype
+is
+.Dv AF_INET6 ,
+.Li h_length
+is 16,
+.Li h_addr_list[0]
+is a pointer to the 16-byte binary address, and
+.Li h_addr_list[1]
+is a
+.Dv NULL
+pointer.
+.Pp
+It is an error when
+.Ar name
+is an IPv6 hex address and
+.Ar af
+equals
+.Dv AF_INET .
+The function's return value is a
+.Dv NULL
+pointer and the value pointed to by
+.Ar error_num
+equals
+.Dv HOST_NOT_FOUND .
+.Pp
+.Fn getipnodebyaddr
+takes almost the same argument as
+.Xr gethostbyaddr 3 ,
+but adds a pointer to return an error number.
+Additionally it takes care of IPv4-mapped IPv6 addresses,
+and IPv4-compatible IPv6 addresses.
+.Pp
+.Fn getipnodebyname
+and
+.Fn getipnodebyaddr
+dynamically allocate the structure to be returned to the caller.
+.Fn freehostent
+reclaims memory region allocated and returned by
+.Fn getipnodebyname
+or
+.Fn getipnodebyaddr .
+.\"
+.Sh FILES
+.Bl -tag -width /etc/resolv.conf -compact
+.It Pa /etc/hosts
+.It Pa /etc/host.conf
+.It Pa /etc/resolv.conf
+.El
+.\"
+.Sh DIAGNOSTICS
+.Nm getipnodebyname
+and
+.Nm getipnodebyaddr
+returns
+.Dv NULL
+on errors.
+The integer values pointed to by
+.Ar error_num
+may then be checked to see whether this is a temporary failure
+or an invalid or unknown host.
+The meanings of each error code are described in
+.Xr gethostbyname 3 .
+.\"
+.Sh SEE ALSO
+.Xr gethostbyname 3 ,
+.Xr gethostbyaddr 3 ,
+.Xr hosts 5 ,
+.Xr services 5 ,
+.Xr hostname 7 ,
+.Xr named 8
+.Pp
+R. Gilligan, S. Thomson, J. Bound, and W. Stevens,
+``Basic Socket Interface Extensions for IPv6,'' RFC2553, March 1999.
+.\"
+.Sh HISTORY
+The implementation first appeared in KAME advanced networking kit.
+.\"
+.Sh STANDARDS
+.Fn getipnodebyname
+and
+.Fn getipnodebyaddr
+are documented in ``Basic Socket Interface Extensions for IPv6''
+.Pq RFC2553 .
+.\"
+.Sh BUGS
+The text was shamelessly copied from RFC2553.
diff --git a/lib/libc/net/getnameinfo.3 b/lib/libc/net/getnameinfo.3
new file mode 100644
index 0000000..c110ac9
--- /dev/null
+++ b/lib/libc/net/getnameinfo.3
@@ -0,0 +1,232 @@
+.\" Copyright (c) 1983, 1987, 1991, 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.
+.\"
+.\" From: @(#)gethostbyname.3 8.4 (Berkeley) 5/25/95
+.\" $Id: getnameinfo.3,v 1.2 1999/10/07 04:46:27 itojun Exp $
+.\" $FreeBSD$
+.\"
+.Dd May 25, 1995
+.Dt GETNAMEINFO 3
+.Os KAME
+.\"
+.Sh NAME
+.Nm getnameinfo
+.Nd address-to-nodename translation in protocol-independent manner
+.\"
+.Sh SYNOPSIS
+.Fd #include <sys/socket.h>
+.Fd #include <netdb.h>
+.Ft int
+.Fn getnameinfo "const struct sockaddr *sa" "socklen_t salen" \
+"char *host" "size_t hostlen" "char *serv" "size_t servlen" "int flags"
+.\"
+.Sh DESCRIPTION
+The
+.Fn getnameinfo
+function is defined for protocol-independent address-to-nodename translation.
+Its functionality is a reverse conversion of
+.Xr getaddrinfo 3 ,
+and implements similar functionality with
+.Xr gethostbyaddr 3 and
+.Xr getservbyport 3
+in more sophisticated manner.
+.Pp
+This function looks up an IP address and port number provided by the
+caller in the DNS and system-specific database, and returns text
+strings for both in buffers provided by the caller.
+The function indicates successful completion by a zero return value;
+a non-zero return value indicates failure.
+.Pp
+The first argument,
+.Fa sa ,
+points to either a
+.Fa sockaddr_in
+structure (for IPv4) or a
+.Fa sockaddr_in6
+structure (for IPv6) that holds the IP address and port number.
+The
+.Fa salen
+argument gives the length of the
+.Fa sockaddr_in
+or
+.Fa sockaddr_in6
+structure.
+.Pp
+The function returns the nodename associated with the IP address in
+the buffer pointed to by the
+.Fa host
+argument.
+The caller provides the size of this buffer via the
+.Fa hostlen
+argument.
+The service name associated with the port number is returned in the buffer
+pointed to by
+.Fa serv ,
+and the
+.Fa servlen
+argument gives the length of this buffer.
+The caller specifies not to return either string by providing a zero
+value for the
+.Fa hostlen
+or
+.Fa servlen
+arguments.
+Otherwise, the caller must provide buffers large enough to hold the
+nodename and the service name, including the terminating null characters.
+.Pp
+Unfortunately most systems do not provide constants that specify the
+maximum size of either a fully-qualified domain name or a service name.
+Therefore to aid the application in allocating buffers for these two
+returned strings the following constants are defined in
+.Li <netdb.h> :
+.Bd -literal -offset
+#define NI_MAXHOST 1025
+#define NI_MAXSERV 32
+.Ed
+.Pp
+The first value is actually defined as the constant
+.Dv MAXDNAME
+in recent versions of BIND's
+.Li <arpa/nameser.h>
+header
+.Po
+older versions of BIND define this constant to be 256
+.Pc
+and the second is a guess based on the services listed in the current
+Assigned Numbers RFC.
+.Pp
+The final argument is a
+.Fa flag
+that changes the default actions of this function.
+By default the fully-qualified domain name (FQDN) for the host is
+looked up in the DNS and returned.
+If the flag bit
+.Dv NI_NOFQDN
+is set, only the nodename portion of the FQDN is returned for local hosts.
+.Pp
+If the
+.Fa flag
+bit
+.Dv NI_NUMERICHOST
+is set, or if the host's name cannot be located in the DNS,
+the numeric form of the host's address is returned instead of its name
+.Po
+e.g., by calling
+.Fn inet_ntop
+instead of
+.Fn getnodebyaddr
+.Pc .
+If the
+.Fa flag
+bit
+.Dv NI_NAMEREQD
+is set, an error is returned if the host's name cannot be located in the DNS.
+.Pp
+If the flag bit
+.Dv NI_NUMERICSERV
+is set, the numeric form of the service address is returned
+.Pq e.g., its port number
+instead of its name.
+The two
+.Dv NI_NUMERICxxx
+flags are required to support the
+.Li "-n"
+flag that many commands provide.
+.Pp
+A fifth flag bit,
+.Dv NI_DGRAM ,
+specifies that the service is a datagram service, and causes
+.Fn getservbyport
+to be called with a second argument of "udp" instead of its default of "tcp".
+This is required for the few ports (512-514)
+that have different services for UDP and TCP.
+.Pp
+These
+.Dv NI_xxx
+flags are defined in
+.Li <netdb.h> .
+.\"
+.Sh EXTENSION
+The implementation allows experimental numeric IPv6 address notation with
+scope identifier.
+IPv6 link-local address will appear as string like
+.Dq Li fe80::1@ne0 ,
+if
+.Dv NI_WITHSCOPEID
+bit is enabled in
+.Ar flags
+argument.
+Refer to
+.Xr getaddrinfo 3
+for the notation.
+.\"
+.Sh FILES
+.Bl -tag -width /etc/resolv.conf -compact
+.It Pa /etc/hosts
+.It Pa /etc/host.conf
+.It Pa /etc/resolv.conf
+.El
+.\"
+.Sh DIAGNOSTICS
+The function indicates successful completion by a zero return value;
+a non-zero return value indicates failure.
+.\"
+.Sh SEE ALSO
+.Xr getaddrinfo 3 ,
+.Xr gethostbyaddr 3 ,
+.Xr getservbyport 3 ,
+.Xr hosts 5 ,
+.Xr services 5 ,
+.Xr hostname 7 ,
+.Xr named 8
+.Pp
+.Rs
+.%A R. Gilligan
+.%A S. Thomson
+.%A J. Bound
+.%A W. Stevens
+.%T Basic Socket Interface Extensions for IPv6
+.%R RFC2553
+.%D March 1999
+.Re
+.\"
+.Sh HISTORY
+The implementation first appeared in WIDE Hydrangea IPv6 protocol stack kit.
+.\"
+.Sh STANDARDS
+The
+.Fn getaddrinfo
+function is defined IEEE POSIX 1003.1g draft specification,
+and documented in ``Basic Socket Interface Extensions for IPv6''
+.Pq RFC2533 .
+.\"
+.Sh BUGS
+The text was shamelessly copied from RFC2553.
diff --git a/lib/libc/net/getnameinfo.c b/lib/libc/net/getnameinfo.c
new file mode 100644
index 0000000..76edc12
--- /dev/null
+++ b/lib/libc/net/getnameinfo.c
@@ -0,0 +1,228 @@
+/*
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Issues to be discussed:
+ * - Thread safe-ness must be checked
+ * - Return values. There seems to be no standard for return value (RFC2553)
+ * but INRIA implementation returns EAI_xxx defined for getaddrinfo().
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <netdb.h>
+#include <resolv.h>
+#include <string.h>
+#include <stddef.h>
+
+#define SUCCESS 0
+#define ANY 0
+#define YES 1
+#define NO 0
+
+static struct afd {
+ int a_af;
+ int a_addrlen;
+ int a_socklen;
+ int a_off;
+} afdl [] = {
+#ifdef INET6
+ {PF_INET6, sizeof(struct in6_addr), sizeof(struct sockaddr_in6),
+ offsetof(struct sockaddr_in6, sin6_addr)},
+#endif
+ {PF_INET, sizeof(struct in_addr), sizeof(struct sockaddr_in),
+ offsetof(struct sockaddr_in, sin_addr)},
+ {0, 0, 0},
+};
+
+struct sockinet {
+ u_char si_len;
+ u_char si_family;
+ u_short si_port;
+};
+
+#define ENI_NOSOCKET 0
+#define ENI_NOSERVHOST 1
+#define ENI_NOHOSTNAME 2
+#define ENI_MEMORY 3
+#define ENI_SYSTEM 4
+#define ENI_FAMILY 5
+#define ENI_SALEN 6
+
+int
+getnameinfo(sa, salen, host, hostlen, serv, servlen, flags)
+ const struct sockaddr *sa;
+ size_t salen;
+ char *host;
+ size_t hostlen;
+ char *serv;
+ size_t servlen;
+ int flags;
+{
+ struct afd *afd;
+ struct servent *sp;
+ struct hostent *hp;
+ u_short port;
+ int family, i;
+ char *addr, *p;
+ u_long v4a;
+ int h_error;
+ char numserv[512];
+ char numaddr[512];
+ int noserv = 0;
+
+ if (sa == NULL)
+ return ENI_NOSOCKET;
+
+ if (sa->sa_len != salen)
+ return ENI_SALEN;
+
+ family = sa->sa_family;
+ for (i = 0; afdl[i].a_af; i++)
+ if (afdl[i].a_af == family) {
+ afd = &afdl[i];
+ goto found;
+ }
+ return ENI_FAMILY;
+
+ found:
+ if (salen != afd->a_socklen)
+ return ENI_SALEN;
+
+ port = ((struct sockinet *)sa)->si_port; /* network byte order */
+ addr = (char *)sa + afd->a_off;
+
+ if (serv == NULL || servlen == 0) {
+ noserv = 1;
+ } else {
+ if (flags & NI_NUMERICSERV)
+ sp = NULL;
+ else {
+ sp = getservbyport(port,
+ (flags & NI_DGRAM) ? "udp" : "tcp");
+ }
+ if (sp) {
+ if (strlen(sp->s_name) > servlen)
+ return ENI_MEMORY;
+ strcpy(serv, sp->s_name);
+ } else {
+ snprintf(numserv, sizeof(numserv), "%d", ntohs(port));
+ if (strlen(numserv) > servlen)
+ return ENI_MEMORY;
+ strcpy(serv, numserv);
+ }
+ }
+
+ switch (sa->sa_family) {
+ case AF_INET:
+ v4a = ntohl(((struct sockaddr_in *)sa)->sin_addr.s_addr);
+ if (IN_MULTICAST(v4a) || IN_EXPERIMENTAL(v4a))
+ flags |= NI_NUMERICHOST;
+ v4a >>= IN_CLASSA_NSHIFT;
+ if (v4a == 0 || v4a == IN_LOOPBACKNET)
+ flags |= NI_NUMERICHOST;
+ break;
+#ifdef INET6
+ case AF_INET6:
+ {
+ struct sockaddr_in6 *sin6;
+ sin6 = (struct sockaddr_in6 *)sa;
+ if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) ||
+ IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
+ flags |= NI_NUMERICHOST;
+ }
+ break;
+#endif
+ }
+ if (host == NULL || hostlen == 0) {
+ if (noserv == 1)
+ return ENI_NOSERVHOST;
+ } else if (flags & NI_NUMERICHOST) {
+ /* NUMERICHOST and NAMEREQD conflicts with each other */
+ if (flags & NI_NAMEREQD)
+ return ENI_NOHOSTNAME;
+ if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr))
+ == NULL)
+ return ENI_SYSTEM;
+ if (strlen(numaddr) > hostlen)
+ return ENI_MEMORY;
+ strcpy(host, numaddr);
+#ifdef INET6
+ if (afd->a_af == AF_INET6 &&
+ (IN6_IS_ADDR_LINKLOCAL((struct in6_addr *)addr) ||
+ IN6_IS_ADDR_MULTICAST((struct in6_addr *)addr)) &&
+ ((struct sockaddr_in6 *)sa)->sin6_scope_id) {
+ if (flags & NI_WITHSCOPEID) {
+ char *ep = strchr(host, '\0');
+ unsigned int ifindex =
+ ((struct sockaddr_in6 *)sa)->sin6_scope_id;
+ char ifname[IF_NAMESIZE * 2 /* for safety */];
+
+ if ((if_indextoname(ifindex, ifname)) == NULL)
+ return ENI_SYSTEM;
+ if (strlen(host) + 1 /* SCOPE_DELIMITER */
+ + strlen(ifname) > hostlen)
+ return ENI_MEMORY;
+ *ep = SCOPE_DELIMITER;
+ strcpy(ep + 1, ifname);
+ }
+ }
+#endif /* INET6 */
+ } else {
+ hp = getipnodebyaddr(addr, afd->a_addrlen, afd->a_af, &h_error);
+ if (hp) {
+ if (flags & NI_NOFQDN) {
+ p = strchr(hp->h_name, '.');
+ if (p) *p = '\0';
+ }
+ if (strlen(hp->h_name) > hostlen) {
+ freehostent(hp);
+ return ENI_MEMORY;
+ }
+ strcpy(host, hp->h_name);
+ freehostent(hp);
+ } else {
+ if (flags & NI_NAMEREQD)
+ return ENI_NOHOSTNAME;
+ if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr))
+ == NULL)
+ return ENI_NOHOSTNAME;
+ if (strlen(numaddr) > hostlen)
+ return ENI_MEMORY;
+ strcpy(host, numaddr);
+ }
+ }
+ return SUCCESS;
+}
diff --git a/lib/libc/net/name6.c b/lib/libc/net/name6.c
new file mode 100644
index 0000000..6683451
--- /dev/null
+++ b/lib/libc/net/name6.c
@@ -0,0 +1,1260 @@
+/*
+ * Copyright (C) 1995, 1996, 1997, 1998, and 1999 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.
+ *
+ * $FreeBSD$
+ */
+/* $Id: name6.c,v 1.9 1999/10/29 03:04:26 itojun Exp $ */
+/*
+ * Atsushi Onoe <onoe@sm.sony.co.jp>
+ */
+
+/*
+ * TODO for thread safe
+ * use mutex for _hostconf, _hostconf_init.
+ * rewrite resolvers to be thread safe
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+
+#include <netdb.h>
+#include <resolv.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifndef _PATH_HOSTS
+#define _PATH_HOSTS "/etc/hosts"
+#endif
+
+#ifndef MAXALIASES
+#define MAXALIASES 10
+#endif
+#ifndef MAXADDRS
+#define MAXADDRS 20
+#endif
+#ifndef MAXDNAME
+#define MAXDNAME 1025
+#endif
+
+#ifdef INET6
+#define ADDRLEN(af) ((af) == AF_INET6 ? sizeof(struct in6_addr) : \
+ sizeof(struct in_addr))
+#else
+#define ADDRLEN(af) sizeof(struct in_addr)
+#endif
+
+#define MAPADDR(ab, ina) \
+do { \
+ memcpy(&(ab)->map_inaddr, ina, sizeof(struct in_addr)); \
+ memset((ab)->map_zero, 0, sizeof((ab)->map_zero)); \
+ memset((ab)->map_one, 0xff, sizeof((ab)->map_one)); \
+} while (0)
+#define MAPADDRENABLED(flags) \
+ (((flags) & AI_V4MAPPED) || \
+ (((flags) & AI_V4MAPPED_CFG) && _mapped_addr_enabled()))
+
+union inx_addr {
+ struct in_addr in_addr;
+#ifdef INET6
+ struct in6_addr in6_addr;
+#endif
+ struct {
+ u_char mau_zero[10];
+ u_char mau_one[2];
+ struct in_addr mau_inaddr;
+ } map_addr_un;
+#define map_zero map_addr_un.mau_zero
+#define map_one map_addr_un.mau_one
+#define map_inaddr map_addr_un.mau_inaddr
+};
+
+static struct hostent *_hpcopy(struct hostent *hp, int *errp);
+static struct hostent *_hpaddr(int af, const char *name, void *addr, int *errp);
+static struct hostent *_hpmerge(struct hostent *hp1, struct hostent *hp2, int *errp);
+#ifdef INET6
+static struct hostent *_hpmapv6(struct hostent *hp, int *errp);
+#endif
+static struct hostent *_hpsort(struct hostent *hp);
+static struct hostent *_ghbyname(const char *name, int af, int flags, int *errp);
+static char *_hgetword(char **pp);
+static int _mapped_addr_enabled(void);
+
+static FILE *_files_open(int *errp);
+static struct hostent *_files_ghbyname(const char *name, int af, int *errp);
+static struct hostent *_files_ghbyaddr(const void *addr, int addrlen, int af, int *errp);
+static void _files_shent(int stayopen);
+static void _files_ehent(void);
+static struct hostent *_dns_ghbyname(const char *name, int af, int *errp);
+static struct hostent *_dns_ghbyaddr(const void *addr, int addrlen, int af, int *errp);
+static void _dns_shent(int stayopen);
+static void _dns_ehent(void);
+#ifdef ICMPNL
+static struct hostent *_icmp_ghbyaddr(const void *addr, int addrlen, int af, int *errp);
+#endif /* ICMPNL */
+
+/*
+ * Select order host function.
+ */
+#define MAXHOSTCONF 4
+
+#ifndef HOSTCONF
+# define HOSTCONF "/etc/host.conf"
+#endif /* !HOSTCONF */
+
+struct _hostconf {
+ struct hostent *(*byname)(const char *name, int af, int *errp);
+ struct hostent *(*byaddr)(const void *addr, int addrlen, int af, int *errp);
+};
+
+/* default order */
+static struct _hostconf _hostconf[MAXHOSTCONF] = {
+ { _dns_ghbyname, _dns_ghbyaddr },
+ { _files_ghbyname, _files_ghbyaddr },
+#ifdef ICMPNL
+ { NULL, _icmp_ghbyaddr },
+#endif /* ICMPNL */
+};
+
+static int _hostconf_init_done;
+static void _hostconf_init(void);
+
+/*
+ * Initialize hostconf structure.
+ */
+
+static void
+_hostconf_init(void)
+{
+ FILE *fp;
+ int n;
+ char *p, *line;
+ char buf[BUFSIZ];
+
+ _hostconf_init_done = 1;
+ n = 0;
+ p = HOSTCONF;
+ if ((fp = fopen(p, "r")) == NULL)
+ return;
+ while (n < MAXHOSTCONF && fgets(buf, sizeof(buf), fp)) {
+ line = buf;
+ if ((p = _hgetword(&line)) == NULL)
+ continue;
+ do {
+ if (strcmp(p, "hosts") == 0
+ || strcmp(p, "local") == 0
+ || strcmp(p, "file") == 0
+ || strcmp(p, "files") == 0) {
+ _hostconf[n].byname = _files_ghbyname;
+ _hostconf[n].byaddr = _files_ghbyaddr;
+ n++;
+ }
+ else if (strcmp(p, "dns") == 0
+ || strcmp(p, "bind") == 0) {
+ _hostconf[n].byname = _dns_ghbyname;
+ _hostconf[n].byaddr = _dns_ghbyaddr;
+ n++;
+ }
+#ifdef ICMPNL
+ else if (strcmp(p, "icmp") == 0) {
+ _hostconf[n].byname = NULL;
+ _hostconf[n].byaddr = _icmp_ghbyaddr;
+ n++;
+ }
+#endif /* ICMPNL */
+ } while ((p = _hgetword(&line)) != NULL);
+ }
+ fclose(fp);
+ if (n < 0) {
+ /* no keyword found. do not change default configuration */
+ return;
+ }
+ for (; n < MAXHOSTCONF; n++) {
+ _hostconf[n].byname = NULL;
+ _hostconf[n].byaddr = NULL;
+ }
+}
+
+/*
+ * Check if kernel supports mapped address.
+ * implementation dependent
+ */
+#ifdef __KAME__
+#include <sys/sysctl.h>
+#endif /* __KAME__ */
+
+static int
+_mapped_addr_enabled(void)
+{
+ /* implementation dependent check */
+#if defined(__KAME__) && defined(IPV6CTL_MAPPED_ADDR)
+ int mib[4];
+ size_t len;
+ int val;
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_INET6;
+ mib[2] = IPPROTO_IPV6;
+ mib[3] = IPV6CTL_MAPPED_ADDR;
+ len = sizeof(val);
+ if (sysctl(mib, 4, &val, &len, 0, 0) == 0 && val != 0)
+ return 1;
+#endif /* __KAME__ && IPV6CTL_MAPPED_ADDR */
+ return 0;
+}
+
+/*
+ * Functions defined in RFC2553
+ * getipnodebyname, getipnodebyadr, freehostent
+ */
+
+static struct hostent *
+_ghbyname(const char *name, int af, int flags, int *errp)
+{
+ struct hostent *hp;
+ int i;
+
+ if (flags & AI_ADDRCONFIG) {
+ int s;
+
+ if ((s = socket(af, SOCK_DGRAM, 0)) < 0)
+ return NULL;
+ /*
+ * TODO:
+ * Note that implementation dependent test for address
+ * configuration should be done everytime called
+ * (or apropriate interval),
+ * because addresses will be dynamically assigned or deleted.
+ */
+ close(s);
+ }
+
+ for (i = 0; i < MAXHOSTCONF; i++) {
+ if (_hostconf[i].byname
+ && (hp = (*_hostconf[i].byname)(name, af, errp))
+ != NULL)
+ return hp;
+ }
+
+ return NULL;
+}
+
+struct hostent *
+getipnodebyname(const char *name, int af, int flags, int *errp)
+{
+ struct hostent *hp;
+ union inx_addr addrbuf;
+
+ if (af != AF_INET
+#ifdef INET6
+ && af != AF_INET6
+#endif
+ )
+ {
+ *errp = NO_RECOVERY;
+ return NULL;
+ }
+
+#ifdef INET6
+ /* special case for literal address */
+ if (inet_pton(AF_INET6, name, &addrbuf) == 1) {
+ if (af != AF_INET6) {
+ *errp = HOST_NOT_FOUND;
+ return NULL;
+ }
+ return _hpaddr(af, name, &addrbuf, errp);
+ }
+#endif
+ if (inet_pton(AF_INET, name, &addrbuf) == 1) {
+ if (af != AF_INET) {
+ if (MAPADDRENABLED(flags)) {
+ MAPADDR(&addrbuf, &addrbuf.in_addr);
+ } else {
+ *errp = HOST_NOT_FOUND;
+ return NULL;
+ }
+ }
+ return _hpaddr(af, name, &addrbuf, errp);
+ }
+
+ if (!_hostconf_init_done)
+ _hostconf_init();
+
+ *errp = HOST_NOT_FOUND;
+ hp = _ghbyname(name, af, flags, errp);
+
+#ifdef INET6
+ if (af == AF_INET6
+ && ((flags & AI_ALL) || hp == NULL)
+ && (MAPADDRENABLED(flags))) {
+ struct hostent *hp2 = _ghbyname(name, AF_INET, flags, errp);
+ if (hp == NULL)
+ hp = _hpmapv6(hp2, errp);
+ else {
+ if (hp2 && strcmp(hp->h_name, hp2->h_name) != 0) {
+ freehostent(hp2);
+ hp2 = NULL;
+ }
+ hp = _hpmerge(hp, hp2, errp);
+ }
+ }
+#endif
+ return _hpsort(hp);
+}
+
+struct hostent *
+getipnodebyaddr(const void *src, size_t len, int af, int *errp)
+{
+ struct hostent *hp;
+ int i;
+#ifdef INET6
+ struct in6_addr addrbuf;
+#else
+ struct in_addr addrbuf;
+#endif
+
+ *errp = HOST_NOT_FOUND;
+
+ switch (af) {
+ case AF_INET:
+ if (len != sizeof(struct in_addr)) {
+ *errp = NO_RECOVERY;
+ return NULL;
+ }
+ if ((long)src & ~(sizeof(struct in_addr) - 1)) {
+ memcpy(&addrbuf, src, len);
+ src = &addrbuf;
+ }
+ if (((struct in_addr *)src)->s_addr == 0)
+ return NULL;
+ break;
+#ifdef INET6
+ case AF_INET6:
+ if (len != sizeof(struct in6_addr)) {
+ *errp = NO_RECOVERY;
+ return NULL;
+ }
+ if ((long)src & ~(sizeof(struct in6_addr) / 2 - 1)) { /*XXX*/
+ memcpy(&addrbuf, src, len);
+ src = &addrbuf;
+ }
+ if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)src)
+ || IN6_IS_ADDR_V4COMPAT((struct in6_addr *)src)) {
+ src = (char *)src +
+ (sizeof(struct in6_addr) - sizeof(struct in_addr));
+ af = AF_INET;
+ len = sizeof(struct in_addr);
+ }
+ break;
+#endif
+ default:
+ *errp = NO_RECOVERY;
+ return NULL;
+ }
+
+ if (!_hostconf_init_done)
+ _hostconf_init();
+ for (i = 0; i < MAXHOSTCONF; i++) {
+ if (_hostconf[i].byaddr
+ && (hp = (*_hostconf[i].byaddr)(src, len, af, errp)) != NULL)
+ return hp;
+ }
+
+ return NULL;
+}
+
+void
+freehostent(struct hostent *ptr)
+{
+ free(ptr);
+}
+
+#if 0
+
+/* XXX: should be deprecated */
+struct hostent *
+getnodebyname(const char *name, int af, int flags)
+{
+ return getipnodebyname(name, af, flags, &h_errno);
+}
+
+#ifdef __warn_references
+__warn_references(getnodebyname,
+ "warning: getnodebyname() deprecated, "
+ "should use getaddrinfo() or getipnodebyname()");
+#endif
+
+struct hostent *
+getnodebyaddr(const void *src, size_t len, int af)
+{
+ return getipnodebyaddr(src, len, af, &h_errno);
+}
+
+#ifdef __warn_references
+__warn_references(getnodebyaddr,
+ "warning: getnodebyaddr() deprecated, "
+ "should use getnameinfo() or getipnodebyaddr()");
+#endif
+
+#endif
+
+/*
+ * Private utility functions
+ */
+
+/*
+ * _hpcopy: allocate and copy hostent structure
+ */
+static struct hostent *
+_hpcopy(struct hostent *hp, int *errp)
+{
+ struct hostent *nhp;
+ char *cp, **pp;
+ int size, addrsize;
+ int nalias = 0, naddr = 0;
+ int al_off;
+ int i;
+
+ if (hp == NULL)
+ return hp;
+
+ /* count size to be allocated */
+ size = sizeof(struct hostent);
+ if (hp->h_name != NULL && *hp->h_name != '\0')
+ size += strlen(hp->h_name) + 1;
+ if ((pp = hp->h_aliases) != NULL) {
+ for (i = 0; *pp != NULL; i++, pp++) {
+ if (**pp != '\0') {
+ size += strlen(*pp) + 1;
+ nalias++;
+ }
+ }
+ }
+ /* adjust alignment */
+ size = ALIGN(size);
+ al_off = size;
+ size += sizeof(char *) * (nalias + 1);
+ addrsize = ALIGN(hp->h_length);
+ if ((pp = hp->h_addr_list) != NULL) {
+ while (*pp++ != NULL)
+ naddr++;
+ }
+ size += addrsize * naddr;
+ size += sizeof(char *) * (naddr + 1);
+
+ /* copy */
+ if ((nhp = (struct hostent *)malloc(size)) == NULL) {
+ *errp = TRY_AGAIN;
+ return NULL;
+ }
+ cp = (char *)&nhp[1];
+ if (hp->h_name != NULL && *hp->h_name != '\0') {
+ nhp->h_name = cp;
+ strcpy(cp, hp->h_name);
+ cp += strlen(cp) + 1;
+ } else
+ nhp->h_name = NULL;
+ nhp->h_aliases = (char **)((char *)nhp + al_off);
+ if ((pp = hp->h_aliases) != NULL) {
+ for (i = 0; *pp != NULL; pp++) {
+ if (**pp != '\0') {
+ nhp->h_aliases[i++] = cp;
+ strcpy(cp, *pp);
+ cp += strlen(cp) + 1;
+ }
+ }
+ }
+ nhp->h_aliases[nalias] = NULL;
+ cp = (char *)&nhp->h_aliases[nalias + 1];
+ nhp->h_addrtype = hp->h_addrtype;
+ nhp->h_length = hp->h_length;
+ nhp->h_addr_list = (char **)cp;
+ if ((pp = hp->h_addr_list) != NULL) {
+ cp = (char *)&nhp->h_addr_list[naddr + 1];
+ for (i = 0; *pp != NULL; pp++) {
+ nhp->h_addr_list[i++] = cp;
+ memcpy(cp, *pp, hp->h_length);
+ cp += addrsize;
+ }
+ }
+ nhp->h_addr_list[naddr] = NULL;
+ return nhp;
+}
+
+/*
+ * _hpaddr: construct hostent structure with one address
+ */
+static struct hostent *
+_hpaddr(int af, const char *name, void *addr, int *errp)
+{
+ struct hostent *hp, hpbuf;
+ char *addrs[2];
+
+ hp = &hpbuf;
+ hp->h_name = (char *)name;
+ hp->h_aliases = NULL;
+ hp->h_addrtype = af;
+ hp->h_length = ADDRLEN(af);
+ hp->h_addr_list = addrs;
+ addrs[0] = (char *)addr;
+ addrs[1] = NULL;
+ return _hpcopy(hp, errp);
+}
+
+/*
+ * _hpmerge: merge 2 hostent structure, arguments will be freed
+ */
+static struct hostent *
+_hpmerge(struct hostent *hp1, struct hostent *hp2, int *errp)
+{
+ int i, j;
+ int naddr, nalias;
+ char **pp;
+ struct hostent *hp, hpbuf;
+ char *aliases[MAXALIASES + 1], *addrs[MAXADDRS + 1];
+ union inx_addr addrbuf[MAXADDRS];
+
+ if (hp1 == NULL)
+ return hp2;
+ if (hp2 == NULL)
+ return hp1;
+
+#define HP(i) (i == 1 ? hp1 : hp2)
+ hp = &hpbuf;
+ hp->h_name = (hp1->h_name != NULL ? hp1->h_name : hp2->h_name);
+ hp->h_aliases = aliases;
+ nalias = 0;
+ for (i = 1; i <= 2; i++) {
+ if ((pp = HP(i)->h_aliases) == NULL)
+ continue;
+ for (; nalias < MAXALIASES && *pp != NULL; pp++) {
+ /* check duplicates */
+ for (j = 0; j < nalias; j++)
+ if (strcasecmp(*pp, aliases[j]) == 0)
+ break;
+ if (j == nalias)
+ aliases[nalias++] = *pp;
+ }
+ }
+ aliases[nalias] = NULL;
+#ifdef INET6
+ if (hp1->h_length != hp2->h_length) {
+ hp->h_addrtype = AF_INET6;
+ hp->h_length = sizeof(struct in6_addr);
+ } else {
+#endif
+ hp->h_addrtype = hp1->h_addrtype;
+ hp->h_length = hp1->h_length;
+#ifdef INET6
+ }
+#endif
+ hp->h_addr_list = addrs;
+ naddr = 0;
+ for (i = 1; i <= 2; i++) {
+ if ((pp = HP(i)->h_addr_list) == NULL)
+ continue;
+ if (HP(i)->h_length == hp->h_length) {
+ while (naddr < MAXADDRS && *pp != NULL)
+ addrs[naddr++] = *pp++;
+ } else {
+ /* copy IPv4 addr as mapped IPv6 addr */
+ while (naddr < MAXADDRS && *pp != NULL) {
+ MAPADDR(&addrbuf[naddr], *pp++);
+ addrs[naddr] = (char *)&addrbuf[naddr];
+ naddr++;
+ }
+ }
+ }
+ addrs[naddr] = NULL;
+ hp = _hpcopy(hp, errp);
+ freehostent(hp1);
+ freehostent(hp2);
+ return hp;
+}
+
+/*
+ * _hpmapv6: convert IPv4 hostent into IPv4-mapped IPv6 addresses
+ */
+#ifdef INET6
+static struct hostent *
+_hpmapv6(struct hostent *hp, int *errp)
+{
+ struct hostent *hp6;
+
+ if (hp == NULL)
+ return NULL;
+ if (hp->h_addrtype == AF_INET6)
+ return hp;
+
+ /* make dummy hostent to convert IPv6 address */
+ if ((hp6 = (struct hostent *)malloc(sizeof(struct hostent))) == NULL) {
+ *errp = TRY_AGAIN;
+ return NULL;
+ }
+ hp6->h_name = NULL;
+ hp6->h_aliases = NULL;
+ hp6->h_addrtype = AF_INET6;
+ hp6->h_length = sizeof(struct in6_addr);
+ hp6->h_addr_list = NULL;
+ return _hpmerge(hp6, hp, errp);
+}
+#endif
+
+/*
+ * _hpsort: sort address by sortlist
+ */
+static struct hostent *
+_hpsort(struct hostent *hp)
+{
+ int i, j, n;
+ u_char *ap, *sp, *mp, **pp;
+ char t;
+ char order[MAXADDRS];
+ int nsort = _res.nsort;
+
+ if (hp == NULL || hp->h_addr_list[1] == NULL || nsort == 0)
+ return hp;
+ for (i = 0; (ap = (u_char *)hp->h_addr_list[i]); i++) {
+ for (j = 0; j < nsort; j++) {
+#ifdef INET6
+ if (_res_ext.sort_list[j].af != hp->h_addrtype)
+ continue;
+ sp = (u_char *)&_res_ext.sort_list[j].addr;
+ mp = (u_char *)&_res_ext.sort_list[j].mask;
+#else
+ sp = (u_char *)&_res.sort_list[j].addr;
+ mp = (u_char *)&_res.sort_list[j].mask;
+#endif
+ for (n = 0; n < hp->h_length; n++) {
+ if ((ap[n] & mp[n]) != sp[n])
+ break;
+ }
+ if (n == hp->h_length)
+ break;
+ }
+ order[i] = j;
+ }
+ n = i;
+ pp = (u_char **)hp->h_addr_list;
+ for (i = 0; i < n - 1; i++) {
+ for (j = i + 1; j < n; j++) {
+ if (order[i] > order[j]) {
+ ap = pp[i];
+ pp[i] = pp[j];
+ pp[j] = ap;
+ t = order[i];
+ order[i] = order[j];
+ order[j] = t;
+ }
+ }
+ }
+ return hp;
+}
+
+static char *
+_hgetword(char **pp)
+{
+ char c, *p, *ret;
+ const char *sp;
+ static const char sep[] = "# \t\n";
+
+ ret = NULL;
+ for (p = *pp; (c = *p) != '\0'; p++) {
+ for (sp = sep; *sp != '\0'; sp++) {
+ if (c == *sp)
+ break;
+ }
+ if (c == '#')
+ p[1] = '\0'; /* ignore rest of line */
+ if (ret == NULL) {
+ if (*sp == '\0')
+ ret = p;
+ } else {
+ if (*sp != '\0') {
+ *p++ = '\0';
+ break;
+ }
+ }
+ }
+ *pp = p;
+ if (ret == NULL || *ret == '\0')
+ return NULL;
+ return ret;
+}
+
+/*
+ * FILES (/etc/hosts)
+ */
+
+static FILE *
+_files_open(int *errp)
+{
+ FILE *fp;
+ fp = fopen(_PATH_HOSTS, "r");
+ if (fp == NULL)
+ *errp = NO_RECOVERY;
+ return fp;
+}
+
+static struct hostent *
+_files_ghbyname(const char *name, int af, int *errp)
+{
+ int match, nalias;
+ char *p, *line, *addrstr, *cname;
+ FILE *fp;
+ struct hostent *rethp, *hp, hpbuf;
+ char *aliases[MAXALIASES + 1], *addrs[2];
+ union inx_addr addrbuf;
+ char buf[BUFSIZ];
+
+ if ((fp = _files_open(errp)) == NULL)
+ return NULL;
+ rethp = hp = NULL;
+
+ while (fgets(buf, sizeof(buf), fp)) {
+ line = buf;
+ if ((addrstr = _hgetword(&line)) == NULL
+ || (cname = _hgetword(&line)) == NULL)
+ continue;
+ match = (strcasecmp(cname, name) == 0);
+ nalias = 0;
+ while ((p = _hgetword(&line)) != NULL) {
+ if (!match)
+ match = (strcasecmp(p, name) == 0);
+ if (nalias < MAXALIASES)
+ aliases[nalias++] = p;
+ }
+ if (!match)
+ continue;
+ if (inet_pton(af, addrstr, &addrbuf) != 1) {
+ *errp = NO_DATA; /* name found */
+ continue;
+ }
+ hp = &hpbuf;
+ hp->h_name = cname;
+ hp->h_aliases = aliases;
+ aliases[nalias] = NULL;
+ hp->h_addrtype = af;
+ hp->h_length = ADDRLEN(af);
+ hp->h_addr_list = addrs;
+ addrs[0] = (char *)&addrbuf;
+ addrs[1] = NULL;
+ hp = _hpcopy(hp, errp);
+ rethp = _hpmerge(rethp, hp, errp);
+ }
+ fclose(fp);
+ return rethp;
+}
+
+static struct hostent *
+_files_ghbyaddr(const void *addr, int addrlen, int af, int *errp)
+{
+ int nalias;
+ char *p, *line;
+ FILE *fp;
+ struct hostent *hp, hpbuf;
+ char *aliases[MAXALIASES + 1], *addrs[2];
+ union inx_addr addrbuf;
+ char buf[BUFSIZ];
+
+ if ((fp = _files_open(errp)) == NULL)
+ return NULL;
+ hp = NULL;
+ while (fgets(buf, sizeof(buf), fp)) {
+ line = buf;
+ if ((p = _hgetword(&line)) == NULL
+ || inet_pton(af, p, &addrbuf) != 1
+ || memcmp(addr, &addrbuf, addrlen) != 0
+ || (p = _hgetword(&line)) == NULL)
+ continue;
+ hp = &hpbuf;
+ hp->h_name = p;
+ hp->h_aliases = aliases;
+ nalias = 0;
+ while ((p = _hgetword(&line)) != NULL) {
+ if (nalias < MAXALIASES)
+ aliases[nalias++] = p;
+ }
+ aliases[nalias] = NULL;
+ hp->h_addrtype = af;
+ hp->h_length = addrlen;
+ hp->h_addr_list = addrs;
+ addrs[0] = (char *)&addrbuf;
+ addrs[1] = NULL;
+ hp = _hpcopy(hp, errp);
+ break;
+ }
+ fclose(fp);
+ return hp;
+}
+
+#ifdef DEBUG
+#define DNS_ASSERT(X) if (!(X)) { fprintf(stderr, "ASSFAIL: %s %d: %s\n", __FILE__, __LINE__, #X); goto badanswer; }
+#else
+#define DNS_ASSERT(X) if (!(X)) { goto badanswer; }
+#endif
+
+static struct hostent *
+_dns_ghbyname(const char *name, int af, int *errp)
+{
+ int n;
+ u_char answer[BUFSIZ];
+ char tbuf[MAXDNAME+1];
+ HEADER *hp;
+ u_char *cp, *eom;
+ int qtype;
+ int type, class, ancount, qdcount;
+ u_long ttl;
+ char hostbuf[BUFSIZ];
+ char *bp;
+ char *alist[MAXALIASES];
+ char *hlist[MAXADDRS];
+ struct hostent hbuf;
+ int buflen;
+ int na, nh;
+
+ if ((_res.options & RES_INIT) == 0) {
+ if (res_init() < 0) {
+ *errp = h_errno;
+ return NULL;
+ }
+ }
+ hbuf.h_aliases = alist;
+ hbuf.h_addrtype = af;
+ hbuf.h_length = ADDRLEN(af);
+ hbuf.h_addr_list = hlist;
+ na = nh = 0;
+
+#ifdef INET6
+ qtype = (af == AF_INET6 ? T_AAAA : T_A);
+#else
+ qtype = T_A;
+#endif
+ n = res_search(name, C_IN, qtype, answer, sizeof(answer));
+ if (n < 0) {
+ *errp = h_errno;
+ return NULL;
+ }
+ hp = (HEADER *)answer;
+ eom = answer + n;
+ ancount = ntohs(hp->ancount);
+ qdcount = ntohs(hp->qdcount);
+ DNS_ASSERT(qdcount == 1);
+ cp = answer + sizeof(HEADER);
+ bp = hostbuf;
+ buflen = sizeof(hostbuf);
+
+ n = dn_expand(answer, eom, cp, bp, buflen);
+ DNS_ASSERT(n >= 0);
+ cp += n + QFIXEDSZ;
+ hbuf.h_name = bp;
+ n = strlen(bp) + 1;
+ bp += n;
+ buflen -= n;
+ while (ancount-- > 0 && cp < eom) {
+ n = dn_expand(answer, eom, cp, bp, buflen);
+ DNS_ASSERT(n >= 0);
+ cp += n; /* name */
+ type = _getshort(cp);
+ cp += 2; /* type */
+ class = _getshort(cp);
+ cp += 2; /* class */
+ ttl = _getlong(cp);
+ cp += 4; /* ttl */
+ n = _getshort(cp);
+ cp += 2; /* len */
+ DNS_ASSERT(class == C_IN);
+ switch (type) {
+ case T_CNAME:
+ if (na >= MAXALIASES-1) {
+ cp += n;
+ break;
+ }
+ n = dn_expand(answer, eom, cp, tbuf, sizeof(tbuf));
+ DNS_ASSERT(n >= 0);
+ cp += n;
+ /* alias */
+ alist[na++] = bp;
+ n = strlen(bp) + 1;
+ bp += n;
+ buflen -= n;
+ /* canon */
+ n = strlen(tbuf) + 1;
+ DNS_ASSERT(n < buflen);
+ strcpy(bp, tbuf);
+ hbuf.h_name = bp;
+ bp += n;
+ buflen -= n;
+ break;
+ case T_A:
+#ifdef INET6
+ case T_AAAA:
+#endif
+ DNS_ASSERT(type == qtype);
+ bp = (char *)ALIGN(bp);
+ DNS_ASSERT(n == hbuf.h_length);
+ DNS_ASSERT(n < buflen);
+ if (nh < MAXADDRS-1) {
+ hlist[nh++] = bp;
+ memcpy(bp, cp, n);
+ bp += n;
+ buflen -= n;
+ }
+ cp += n;
+ break;
+ default:
+ DNS_ASSERT(0);
+ cp += n;
+ break;
+ }
+ }
+ if (nh == 0) {
+ badanswer:
+ *errp = NO_RECOVERY;
+ return NULL;
+ }
+ alist[na] = NULL;
+ hlist[nh] = NULL;
+ return _hpcopy(&hbuf, errp);
+}
+
+static struct hostent *
+_dns_ghbyaddr(const void *addr, int addrlen, int af, int *errp)
+{
+ int n;
+ u_char answer[BUFSIZ];
+ HEADER *hp;
+ u_char c, *cp, *eom;
+ int type, class, ancount, qdcount;
+ u_long ttl;
+ char hostbuf[BUFSIZ];
+ char *bp;
+ char *alist[MAXALIASES];
+ char *hlist[2];
+ struct hostent hbuf;
+ int buflen;
+ int na;
+#ifdef INET6
+ static const char hex[] = "0123456789abcdef";
+#endif
+
+#ifdef INET6
+ /* XXX */
+ if (af == AF_INET6 && IN6_IS_ADDR_LINKLOCAL((struct in6_addr *)addr))
+ return NULL;
+#endif
+
+ if ((_res.options & RES_INIT) == 0) {
+ if (res_init() < 0) {
+ *errp = h_errno;
+ return NULL;
+ }
+ }
+ hbuf.h_name = NULL;
+ hbuf.h_aliases = alist;
+ hbuf.h_addrtype = af;
+ hbuf.h_length = addrlen;
+ hbuf.h_addr_list = hlist;
+ hlist[0] = (char *)addr;
+ hlist[1] = NULL;
+ na = 0;
+
+ n = 0;
+ bp = hostbuf;
+ cp = (u_char *)addr+addrlen-1;
+ switch (af) {
+#ifdef INET6
+ case AF_INET6:
+ for (; n < addrlen; n++, cp--) {
+ c = *cp;
+ *bp++ = hex[c & 0xf];
+ *bp++ = '.';
+ *bp++ = hex[c >> 4];
+ *bp++ = '.';
+ }
+ strcpy(bp, "ip6.int");
+ break;
+#endif
+ default:
+ for (; n < addrlen; n++, cp--) {
+ c = *cp;
+ if (c >= 100)
+ *bp++ = '0' + c / 100;
+ if (c >= 10)
+ *bp++ = '0' + (c % 100) / 10;
+ *bp++ = '0' + c % 10;
+ *bp++ = '.';
+ }
+ strcpy(bp, "in-addr.arpa");
+ break;
+ }
+
+ n = res_query(hostbuf, C_IN, T_PTR, answer, sizeof(answer));
+ if (n < 0) {
+ *errp = h_errno;
+ return NULL;
+ }
+ hp = (HEADER *)answer;
+ eom = answer + n;
+ ancount = ntohs(hp->ancount);
+ qdcount = ntohs(hp->qdcount);
+ DNS_ASSERT(qdcount == 1);
+ cp = answer + sizeof(HEADER);
+ bp = hostbuf;
+ buflen = sizeof(hostbuf);
+
+ n = dn_expand(answer, eom, cp, bp, buflen);
+ DNS_ASSERT(n >= 0);
+ cp += n + QFIXEDSZ;
+ while (ancount-- > 0 && cp < eom) {
+ n = dn_expand(answer, eom, cp, bp, buflen);
+ DNS_ASSERT(n >= 0);
+ cp += n; /* name */
+ type = _getshort(cp);
+ cp += 2; /* type */
+ class = _getshort(cp);
+ cp += 2; /* class */
+ ttl = _getlong(cp);
+ cp += 4; /* ttl */
+ n = _getshort(cp);
+ cp += 2; /* len */
+ DNS_ASSERT(class == C_IN);
+ switch (type) {
+ case T_PTR:
+ n = dn_expand(answer, eom, cp, bp, buflen);
+ DNS_ASSERT(n >= 0);
+ cp += n;
+ if (na >= MAXALIASES-1)
+ break;
+ if (hbuf.h_name == NULL)
+ hbuf.h_name = bp;
+ else
+ alist[na++] = bp;
+ n = strlen(bp) + 1;
+ bp += n;
+ buflen -= n;
+ break;
+ case T_CNAME:
+ cp += n;
+ break;
+ default:
+ badanswer:
+ *errp = NO_RECOVERY;
+ return NULL;
+ }
+ }
+ if (hbuf.h_name == NULL) {
+ *errp = h_errno;
+ return NULL;
+ }
+ alist[na] = NULL;
+ return _hpcopy(&hbuf, errp);
+}
+
+#ifdef ICMPNL
+
+/*
+ * experimental:
+ * draft-ietf-ipngwg-icmp-namelookups-02.txt
+ * ifindex is assumed to be encoded in addr.
+ */
+#include <sys/uio.h>
+#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
+
+struct _icmp_host_cache {
+ struct _icmp_host_cache *hc_next;
+ int hc_ifindex;
+ struct in6_addr hc_addr;
+ char *hc_name;
+};
+
+static char *
+_icmp_fqdn_query(const struct in6_addr *addr, int ifindex)
+{
+ int s;
+ struct icmp6_filter filter;
+ struct msghdr msg;
+ struct cmsghdr *cmsg;
+ struct in6_pktinfo *pkt;
+ char cbuf[256];
+ char buf[1024];
+ int cc;
+ struct icmp6_fqdn_query *fq;
+ struct icmp6_fqdn_reply *fr;
+ struct _icmp_host_cache *hc;
+ struct sockaddr_in6 sin6;
+ struct iovec iov;
+ fd_set s_fds, fds;
+ struct timeval tout;
+ int len;
+ char *name;
+ static int pid;
+ static struct _icmp_host_cache *hc_head;
+
+ for (hc = hc_head; hc; hc = hc->hc_next) {
+ if (hc->hc_ifindex == ifindex
+ && IN6_ARE_ADDR_EQUAL(&hc->hc_addr, addr))
+ return hc->hc_name;
+ }
+
+ if (pid == 0)
+ pid = getpid();
+
+ ICMP6_FILTER_SETBLOCKALL(&filter);
+ ICMP6_FILTER_SETPASS(ICMP6_FQDN_REPLY, &filter);
+
+ FD_ZERO(&s_fds);
+ tout.tv_sec = 0;
+ tout.tv_usec = 200000; /*XXX: 200ms*/
+
+ fq = (struct icmp6_fqdn_query *)buf;
+ fq->icmp6_fqdn_type = ICMP6_FQDN_QUERY;
+ fq->icmp6_fqdn_code = 0;
+ fq->icmp6_fqdn_cksum = 0;
+ fq->icmp6_fqdn_id = (u_short)pid;
+ fq->icmp6_fqdn_unused = 0;
+ fq->icmp6_fqdn_cookie[0] = 0;
+ fq->icmp6_fqdn_cookie[1] = 0;
+
+ memset(&sin6, 0, sizeof(sin6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_addr = *addr;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_name = (caddr_t)&sin6;
+ msg.msg_namelen = sizeof(sin6);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ iov.iov_base = (caddr_t)buf;
+ iov.iov_len = sizeof(struct icmp6_fqdn_query);
+
+ if (ifindex) {
+ msg.msg_control = cbuf;
+ msg.msg_controllen = sizeof(cbuf);
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
+ cmsg->cmsg_level = IPPROTO_IPV6;
+ cmsg->cmsg_type = IPV6_PKTINFO;
+ pkt = (struct in6_pktinfo *)&cmsg[1];
+ memset(&pkt->ipi6_addr, 0, sizeof(struct in6_addr));
+ pkt->ipi6_ifindex = ifindex;
+ cmsg = CMSG_NXTHDR(&msg, cmsg);
+ msg.msg_controllen = (char *)cmsg - cbuf;
+ }
+
+ if ((s = socket(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0)
+ return NULL;
+ (void)setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER,
+ (char *)&filter, sizeof(filter));
+ cc = sendmsg(s, &msg, 0);
+ if (cc < 0) {
+ close(s);
+ return NULL;
+ }
+ FD_SET(s, &s_fds);
+ for (;;) {
+ fds = s_fds;
+ if (select(s + 1, &fds, NULL, NULL, &tout) <= 0) {
+ close(s);
+ return NULL;
+ }
+ len = sizeof(sin6);
+ cc = recvfrom(s, buf, sizeof(buf), 0,
+ (struct sockaddr *)&sin6, &len);
+ if (cc <= 0) {
+ close(s);
+ return NULL;
+ }
+ if (cc < sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr))
+ continue;
+ if (!IN6_ARE_ADDR_EQUAL(addr, &sin6.sin6_addr))
+ continue;
+ fr = (struct icmp6_fqdn_reply *)(buf + sizeof(struct ip6_hdr));
+ if (fr->icmp6_fqdn_type == ICMP6_FQDN_REPLY)
+ break;
+ }
+ close(s);
+ if (fr->icmp6_fqdn_cookie[1] != 0) {
+ /* rfc1788 type */
+ name = buf + sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr) + 4;
+ len = (buf + cc) - name;
+ } else {
+ len = fr->icmp6_fqdn_namelen;
+ name = fr->icmp6_fqdn_name;
+ }
+ if (len <= 0)
+ return NULL;
+ name[len] = 0;
+
+ if ((hc = (struct _icmp_host_cache *)malloc(sizeof(*hc))) == NULL)
+ return NULL;
+ /* XXX: limit number of cached entries */
+ hc->hc_ifindex = ifindex;
+ hc->hc_addr = *addr;
+ hc->hc_name = strdup(name);
+ hc->hc_next = hc_head;
+ hc_head = hc;
+ return hc->hc_name;
+}
+
+static struct hostent *
+_icmp_ghbyaddr(const void *addr, int addrlen, int af, int *errp)
+{
+ char *hname;
+ int ifindex;
+ struct in6_addr addr6;
+
+ if (af != AF_INET6) {
+ /*
+ * Note: rfc1788 defines Who Are You for IPv4,
+ * but no one implements it.
+ */
+ return NULL;
+ }
+
+ memcpy(&addr6, addr, addrlen);
+ ifindex = (addr6.s6_addr[2] << 8) | addr6.s6_addr[3];
+ addr6.s6_addr[2] = addr6.s6_addr[3] = 0;
+
+ if (!IN6_IS_ADDR_LINKLOCAL(&addr6))
+ return NULL; /*XXX*/
+
+ if ((hname = _icmp_fqdn_query(&addr6, ifindex)) == NULL)
+ return NULL;
+ return _hpaddr(af, hname, &addr6, errp);
+}
+#endif /* ICMPNL */
diff --git a/lib/libc/net/res_init.c b/lib/libc/net/res_init.c
index 8f6e649..d1e6249 100644
--- a/lib/libc/net/res_init.c
+++ b/lib/libc/net/res_init.c
@@ -112,6 +112,9 @@ struct __res_state _res
# endif
;
+#ifdef INET6
+struct __res_state_ext _res_ext;
+#endif /* INET6 */
/*
* Set up default settings. If the configuration file exist, the values
@@ -314,6 +317,9 @@ res_init()
#ifdef RESOLVSORT
if (MATCH(buf, "sortlist")) {
struct in_addr a;
+#ifdef INET6
+ struct in6_addr a6;
+#endif /* INET6 */
cp = buf + sizeof("sortlist") - 1;
while (nsort < MAXRESOLVSORT) {
@@ -347,8 +353,61 @@ res_init()
_res.sort_list[nsort].mask =
net_mask(_res.sort_list[nsort].addr);
}
+#ifdef INET6
+ _res_ext.sort_list[nsort].af = AF_INET;
+ _res_ext.sort_list[nsort].addr.ina =
+ _res.sort_list[nsort].addr;
+ _res_ext.sort_list[nsort].mask.ina.s_addr =
+ _res.sort_list[nsort].mask;
+#endif /* INET6 */
nsort++;
}
+#ifdef INET6
+ else if (inet_pton(AF_INET6, net, &a6) == 1) {
+ int m, i;
+ u_char *u;
+
+ _res_ext.sort_list[nsort].af = AF_INET6;
+ _res_ext.sort_list[nsort].addr.in6a = a6;
+ u = (u_char *)&_res_ext.sort_list[nsort].mask.in6a;
+ *cp++ = n;
+ net = cp;
+ while (*cp && *cp != ';' &&
+ isascii(*cp) && !isspace(*cp))
+ cp++;
+ m = n;
+ n = *cp;
+ *cp = 0;
+ switch (m) {
+ case '/':
+ m = atoi(net);
+ break;
+ case '&':
+ if (inet_pton(AF_INET6, net, u) == 1) {
+ m = -1;
+ break;
+ }
+ /*FALLTHROUGH*/
+ default:
+ m = sizeof(struct in6_addr) * NBBY;
+ break;
+ }
+ if (m >= 0) {
+ for (i = 0; i < sizeof(struct in6_addr); i++) {
+ if (m <= 0) {
+ *u = 0;
+ } else {
+ m -= NBBY;
+ *u = (u_char)~0;
+ if (m < 0)
+ *u <<= -m;
+ }
+ u++;
+ }
+ }
+ nsort++;
+ }
+#endif /* INET6 */
*cp = n;
}
continue;
diff --git a/sbin/Makefile b/sbin/Makefile
index 178dd9c..bedcd44 100644
--- a/sbin/Makefile
+++ b/sbin/Makefile
@@ -50,11 +50,13 @@ SUBDIR= adjkerntz \
nologin \
nos-tun \
ping \
+ ping6 \
quotacheck \
reboot \
restore \
route \
routed \
+ rtsol \
savecore \
shutdown \
slattach \
diff --git a/sbin/ping6/Makefile b/sbin/ping6/Makefile
new file mode 100644
index 0000000..0cac4c0
--- /dev/null
+++ b/sbin/ping6/Makefile
@@ -0,0 +1,25 @@
+# Copyright (c) 1996 WIDE Project. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modifications, are permitted provided that the above copyright notice
+# and this paragraph are duplicated in all such forms and that any
+# documentation, advertising materials, and other materials related to
+# such distribution and use acknowledge that the software was developed
+# by the WIDE Project, Japan. The name of the Project may not be used to
+# endorse or promote products derived from this software without
+# specific prior written permission. THIS SOFTWARE IS PROVIDED ``AS IS''
+# AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+# LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE.
+# $FreeBSD$
+
+PROG= ping6
+MAN8= ping6.8
+BINMODE = 4555
+COPTS+= -Wall -Wmissing-prototypes
+.if ${MACHINE_ARCH} == "alpha"
+COPTS+= -fno-builtin # GCC's builtin memcpy doesn't do unaligned copies
+.endif
+CFLAGS+= -DINET6
+
+.include <bsd.prog.mk>
diff --git a/sbin/ping6/ping6.8 b/sbin/ping6/ping6.8
new file mode 100644
index 0000000..76509fa
--- /dev/null
+++ b/sbin/ping6/ping6.8
@@ -0,0 +1,332 @@
+.\" 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.
+.\"
+.\" $Id: ping6.8,v 1.4 1999/10/07 05:29:03 itojun Exp $
+.\" $FreeBSD$
+.\"
+.Dd May 17, 1998
+.Dt PING6 8
+.Os KAME
+.Sh NAME
+.Nm ping6
+.Nd send
+.Tn ICMPv6 ECHO_REQUEST
+packets to network hosts
+.Sh SYNOPSIS
+.Nm
+.Op Fl AdEfnqRrvw
+.Op Fl a Ar addrtype
+.Op Fl b Ar bufsiz
+.Op Fl c Ar count
+.Op Fl h Ar hoplimit
+.Op Fl I Ar interface
+.Op Fl i Ar wait
+.Op Fl l Ar preload
+.Op Fl p Ar pattern
+.Op Fl s Ar packetsize
+.Ar host
+.Sh DESCRIPTION
+.Bl -tag -width Ds
+.It Fl A
+Enables transport-mode IPsec authentication header. (experimental)
+.It Fl a Ar addrtype
+Generate ICMPv6 Node Information Node Addresses query, rather than echo-request.
+.Ar addrtype
+must be a string constructed of the following charaters.
+.Bl -tag -width Ds -compact
+.It Ic a
+requires all the responder's unicast addresses. If the charater is ommited,
+only those addresses which belong to the interface which has the
+responder's address are required.
+.It Ic g
+requires responder's global-scope addresses.
+.It Ic s
+requires responder's site-local addresses.
+.It Ic l
+requires responder's link-local addresses.
+.It Ic A
+requires responder's anycast addresses. Without this character, the responder
+will return unicast addresses only. With this character, the responder
+will return anycast addresses only.
+Note that the specification does not specify how to get responder's
+anycast addresses. This is an experimental option.
+.El
+.It Fl b Ar bufsiz
+Set socket buffer size.
+.It Fl c Ar count
+Stop after sending
+.Pq and receiving
+.Ar count
+.Tn ECHO_RESPONSE
+packets.
+.It Fl d
+Set the
+.Dv SO_DEBUG
+option on the socket being used.
+.It Fl E
+Enables transport-mode IPsec encapsulated security payload. (experimental)
+.It Fl f
+Flood ping.
+Outputs packets as fast as they come back or one hundred times per second,
+whichever is more.
+For every
+.Tn ECHO_REQUEST
+sent a period
+.Dq \&.
+is printed, while for every
+.Tn ECHO_REPLY
+received a backspace is printed.
+This provides a rapid display of how many packets are being dropped.
+Only the super-user may use this option.
+.Bf -emphasis
+This can be very hard on a network and should be used with caution.
+.Ef
+.It Fl h Ar hoplimit
+Set the IPv6 hoplimit.
+.It Fl I Ar interface
+Source packets with the given interface address.
+This flag applies if the ping destination is a multicast address,
+or link-local/site-local unicast address.
+.It Fl i Ar wait
+Wait
+.Ar wait
+seconds
+.Em between sending each packet .
+The default is to wait for one second between each packet.
+This option is incompatible with the
+.Fl f
+option.
+.It Fl l Ar preload
+If
+.Ar preload
+is specified,
+.Nm ping
+sends that many packets as fast as possible before falling into its normal
+mode of behavior.
+Only the super-user may use this option.
+.It Fl n
+Numeric output only.
+No attempt will be made to lookup symbolic names for host addresses.
+.It Fl p Ar pattern
+You may specify up to 16
+.Dq pad
+bytes to fill out the packet you send.
+This is useful for diagnosing data-dependent problems in a network.
+For example,
+.Dq Li \-p ff
+will cause the sent packet to be filled with all
+ones.
+.Fl Q
+flag,
+.Nm
+prints out any ICMP error messages caused by its own ECHO_REQUEST
+messages.
+.It Fl q
+Quiet output.
+Nothing is displayed except the summary lines at startup time and
+when finished.
+.It Fl R
+Record route.
+Includes the
+.Tn RECORD_ROUTE
+option in the
+.Tn ECHO_REQUEST
+packet and displays
+the route buffer on returned packets.
+Note that the IP header is only large enough for nine such routes;
+the
+.Xr traceroute 8
+command is usually better at determining the route packets take to a
+particular destination.
+Many hosts ignore or discard the
+.Tn RECORD_ROUTE
+option.
+.It Fl r
+Bypass the normal routing tables and send directly to a host on an attached
+network.
+If the host is not on a directly-attached network, an error is returned.
+This option can be used to ping a local host through an interface
+that has no route through it
+.Po
+e.g., after the interface was dropped by
+.Xr routed 8
+.Pc .
+.It Fl s Ar packetsize
+Specifies the number of data bytes to be sent.
+The default is 56, which translates into 64
+.Tn ICMP
+data bytes when combined
+with the 8 bytes of
+.Tn ICMP
+header data.
+You may need to specify
+.Fl b
+as well to extend socket buffer size.
+.It Fl v
+Verbose output.
+.Tn ICMP
+packets other than
+.Tn ECHO_RESPONSE
+that are received are listed.
+.It Fl w
+Generate ICMPv6 Node Information FQDN query, rather than echo-request.
+.Fl s
+has no effect if
+.Fl w
+is specified.
+.It Fl W
+Same as
+.Fl w .
+This option was remained for backward compatibility.
+.Fl s
+has no effect if
+.Fl w
+is specified.
+.El
+.Pp
+When using
+.Nm
+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
+.Dq 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 round-trip time statistics.
+When the specified number of packets have been sent
+.Pq and received
+or if the program is terminated with a
+.Dv SIGINT ,
+a brief summary is displayed, showing the number of packets sent and
+received, and the minimum, maximum, mean, and standard deviation of
+the round-trip times.
+.Pp
+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
+.Nm
+during normal operations or from automated scripts.
+.\" .Sh ICMP PACKET DETAILS
+.\" An IP header without options is 20 bytes.
+.\" An
+.\" .Tn ICMP
+.\" .Tn ECHO_REQUEST
+.\" packet contains an additional 8 bytes worth of
+.\" .Tn ICMP
+.\" header followed by an arbitrary amount of data.
+.\" When a
+.\" .Ar packetsize
+.\" is given, this indicated the size of this extra piece of data
+.\" .Pq the default is 56 .
+.\" Thus the amount of data received inside of an IP packet of type
+.\" .Tn ICMP
+.\" .Tn ECHO_REPLY
+.\" will always be 8 bytes more than the requested data space
+.\" .Pq the Tn ICMP header .
+.\" .Pp
+.\" If the data space is at least eight bytes large,
+.\" .Nm
+.\" uses the first eight bytes of this space to include a timestamp which
+.\" it uses in the computation of round trip times.
+.\" If less than eight bytes of pad are specified, no round trip times are
+.\" given.
+.Sh DUPLICATE AND DAMAGED PACKETS
+.Nm Ping6
+will report duplicate and damaged packets.
+Duplicate packets should never occur when pinging a unicast address,
+and seem to be caused by
+inappropriate link-level retransmissions.
+Duplicates may occur in many situations and are rarely
+.Pq if ever
+a good sign, although the presence of low levels of duplicates may not
+always be cause for alarm.
+Duplicates are expected when pinging a broadcast or multicast address,
+since they are not really duplicates but replies from different hosts
+to the same request.
+.Pp
+Damaged packets are obviously serious cause for alarm and often
+indicate broken hardware somewhere in the
+.Nm ping
+packet's path
+.Pq in the network or in the hosts .
+.Sh TRYING DIFFERENT DATA PATTERNS
+The
+(inter)network
+layer should never treat packets differently depending on the data
+contained in the data portion.
+Unfortunately, data-dependent problems have been known to sneak into
+networks and remain undetected for long periods of time.
+In many cases the particular pattern that will have problems is something
+that does not have sufficient
+.Dq transitions ,
+such as all ones or all zeros, or a pattern right at the edge, such as
+almost all zeros.
+It is not
+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.
+.Pp
+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
+cannot
+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
+.Fl p
+option of
+.Nm Ns .
+.Sh RETURN VALUES
+The
+.Nm
+command returns an exit status of zero if at least one response was
+heard from the specified
+.Ar host ;
+a status of two if the transmission was successful but no responses
+were received; or another value
+.Pq from Aq Pa sysexits.h
+if an error occurred.
+.Sh SEE ALSO
+.Xr netstat 1 ,
+.Xr ifconfig 8 ,
+.Xr routed 8 ,
+.Xr ping 8 ,
+.Xr traceroute 8 ,
+.Xr traceroute6 8
+.Sh HISTORY
+The
+.Nm ping
+command appeared in
+.Bx 4.3 .
+.Nm Ping6
+command with IPv6 support first appeared in WIDE Hydrangea IPv6 protocol stack
+kit.
+.\" .Sh BUGS
+.\" (to be written)
diff --git a/sbin/ping6/ping6.c b/sbin/ping6/ping6.c
new file mode 100644
index 0000000..d118655
--- /dev/null
+++ b/sbin/ping6/ping6.c
@@ -0,0 +1,1549 @@
+/*
+ * 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.
+ */
+
+/* BSDI ping.c,v 2.3 1996/01/21 17:56:50 jch Exp */
+
+/*
+ * Copyright (c) 1989, 1993
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)ping.c 8.1 (Berkeley) 6/5/93";
+#endif
+#endif /* not lint */
+
+/*
+ * 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.
+ */
+/*
+ * NOTE:
+ * USE_SIN6_SCOPE_ID assumes that sin6_scope_id has the same semantics
+ * as IPV6_PKTINFO. Some objects it (sin6_scope_id specifies *link* while
+ * IPV6_PKTINFO specifies *interface*. Link is defined as collection of
+ * network attached to 1 or more interfaces)
+ */
+
+#include <sys/param.h>
+#include <sys/uio.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef IPSEC
+#include <netinet6/ah.h>
+#include <netinet6/ipsec.h>
+#endif
+
+#define MAXPACKETLEN 131072
+#define IP6LEN 40
+#define ICMP6ECHOLEN 8 /* icmp echo header len excluding time */
+#define ICMP6ECHOTMLEN sizeof(struct timeval)
+#define ICMP6_NIQLEN (ICMP6ECHOLEN + 8)
+#define ICMP6_NIRLEN (ICMP6ECHOLEN + 12) /* 64 bits of nonce + 32 bits ttl */
+#define EXTRA 256 /* for AH and various other headers. weird. */
+#define DEFDATALEN ICMP6ECHOTMLEN
+#define MAXDATALEN MAXPACKETLEN - IP6LEN - ICMP6ECHOLEN
+#define NROUTES 9 /* number of record route slots */
+
+#define A(bit) rcvd_tbl[(bit)>>3] /* identify byte in array */
+#define B(bit) (1 << ((bit) & 0x07)) /* identify bit in byte */
+#define SET(bit) (A(bit) |= B(bit))
+#define CLR(bit) (A(bit) &= (~B(bit)))
+#define TST(bit) (A(bit) & B(bit))
+
+#define F_FLOOD 0x0001
+#define F_INTERVAL 0x0002
+#define F_NUMERIC 0x0004
+#define F_PINGFILLED 0x0008
+#define F_QUIET 0x0010
+#define F_RROUTE 0x0020
+#define F_SO_DEBUG 0x0040
+#define F_VERBOSE 0x0100
+#ifdef IPSEC
+#ifdef IPSEC_POLICY_IPSEC
+#define F_POLICY 0x4000
+#else
+#define F_AUTHHDR 0x0200
+#define F_ENCRYPT 0x0400
+#endif /*IPSEC_POLICY_IPSEC*/
+#endif /*IPSEC*/
+#define F_NODEADDR 0x0800
+#define F_FQDN 0x1000
+#define F_INTERFACE 0x2000
+u_int options;
+
+#define IN6LEN sizeof(struct in6_addr)
+#define SA6LEN sizeof(struct sockaddr_in6)
+#define DUMMY_PORT 10101
+
+#define SIN6(s) ((struct sockaddr_in6 *)(s))
+
+/*
+ * 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. Change 128
+ * to 8192 for complete accuracy...
+ */
+#define MAX_DUP_CHK (8 * 8192)
+int mx_dup_ck = MAX_DUP_CHK;
+char rcvd_tbl[MAX_DUP_CHK / 8];
+
+struct addrinfo *res;
+struct sockaddr_in6 dst; /* who to ping6 */
+struct sockaddr_in6 src; /* src addr of this packet */
+int datalen = DEFDATALEN;
+int s; /* socket file descriptor */
+u_char outpack[MAXPACKETLEN];
+char BSPACE = '\b'; /* characters written for flood */
+char DOT = '.';
+char *hostname;
+int ident; /* process id to identify our packets */
+
+/* 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 */
+int interval = 1; /* interval between packets */
+int hoplimit = -1; /* hoplimit */
+
+/* timing */
+int timing; /* flag to do timing */
+double tmin = 999999999.0; /* minimum round trip time */
+double tmax = 0.0; /* maximum round trip time */
+double tsum = 0.0; /* sum of all times, for doing average */
+
+/* for inet_ntop() */
+char ntop_buf[INET6_ADDRSTRLEN];
+
+/* for node addresses */
+u_short naflags;
+
+/* for ancillary data(advanced API) */
+struct msghdr smsghdr;
+struct iovec smsgiov;
+char *scmsg = 0;
+
+int main __P((int, char *[]));
+void fill __P((char *, char *));
+int get_hoplim __P((struct msghdr *));
+void onalrm __P((int));
+void oninfo __P((int));
+void onint __P((int));
+void pinger __P((void));
+char *pr_addr __P((struct sockaddr_in6 *));
+void pr_icmph __P((struct icmp6_hdr *, u_char *));
+void pr_iph __P((struct ip6_hdr *));
+void pr_nodeaddr __P((struct icmp6_nodeinfo *, int));
+void pr_pack __P((u_char *, int, struct msghdr *));
+void pr_retip __P((struct ip6_hdr *, u_char *));
+void summary __P((void));
+void tvsub __P((struct timeval *, struct timeval *));
+int setpolicy __P((int, char *));
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ extern int errno, optind;
+ extern char *optarg;
+ struct itimerval itimer;
+ struct sockaddr_in6 from;
+ struct timeval timeout;
+ struct addrinfo hints;
+ fd_set fdset;
+ register int cc, i;
+ int ch, fromlen, hold, packlen, preload, optval, ret_ga;
+ u_char *datap, *packet;
+ char *e, *target, *ifname = NULL;
+ int ip6optlen = 0;
+ struct cmsghdr *scmsgp = NULL;
+ int sockbufsize = 0;
+#ifdef IPSEC_POLICY_IPSEC
+ char *policy_in = NULL;
+ char *policy_out = NULL;
+#endif
+
+ /* just to be sure */
+ memset(&smsghdr, 0, sizeof(&smsghdr));
+ memset(&smsgiov, 0, sizeof(&smsgiov));
+
+ preload = 0;
+ datap = &outpack[ICMP6ECHOLEN + ICMP6ECHOTMLEN];
+#ifndef IPSEC
+ while ((ch = getopt(argc, argv, "a:b:c:dfh:I:i:l:np:qRrs:vwW")) != EOF)
+#else
+#ifdef IPSEC_POLICY_IPSEC
+ while ((ch = getopt(argc, argv, "a:b:c:dfh:I:i:l:np:qRrs:vwWP:")) != EOF)
+#else
+ while ((ch = getopt(argc, argv, "a:b:c:dfh:I:i:l:np:qRrs:vwWAE")) != EOF)
+#endif /*IPSEC_POLICY_IPSEC*/
+#endif
+ switch(ch) {
+ case 'a':
+ {
+ char *cp;
+
+ options |= F_NODEADDR;
+ datalen = 2048; /* XXX: enough? */
+ for (cp = optarg; *cp != '\0'; cp++) {
+ switch(*cp) {
+ case 'a':
+ naflags |= NI_NODEADDR_FLAG_ALL;
+ break;
+ case 'l':
+ case 'L':
+ naflags |= NI_NODEADDR_FLAG_LINKLOCAL;
+ break;
+ case 's':
+ case 'S':
+ naflags |= NI_NODEADDR_FLAG_SITELOCAL;
+ break;
+ case 'g':
+ case 'G':
+ naflags |= NI_NODEADDR_FLAG_GLOBAL;
+ break;
+ case 'A': /* experimental. not in the spec */
+ naflags |= NI_NODEADDR_FLAG_ANYCAST;
+ break;
+ default:
+ usage();
+ }
+ }
+ break;
+ }
+ case 'b':
+#if defined(SO_SNDBUF) && defined(SO_RCVBUF)
+ sockbufsize = atoi(optarg);
+#else
+ err(1,
+"-b option ignored: SO_SNDBUF/SO_RCVBUF socket options not supported");
+#endif
+ break;
+ case 'c':
+ npackets = strtol(optarg, &e, 10);
+ if (npackets <= 0 || *optarg == '\0' || *e != '\0')
+ errx(1,
+ "illegal number of packets -- %s", optarg);
+ break;
+ case 'd':
+ options |= F_SO_DEBUG;
+ break;
+ case 'f':
+ if (getuid()) {
+ errno = EPERM;
+ errx(1, "Must be superuser to flood ping");
+ }
+ options |= F_FLOOD;
+ setbuf(stdout, (char *)NULL);
+ break;
+ case 'h': /* hoplimit */
+ hoplimit = strtol(optarg, &e, 10);
+ if (255 < hoplimit || hoplimit < -1)
+ errx(1,
+ "illegal hoplimit -- %s", optarg);
+ break;
+ case 'I':
+ ifname = optarg;
+ options |= F_INTERFACE;
+ break;
+ case 'i': /* wait between sending packets */
+ interval = strtol(optarg, &e, 10);
+ if (interval <= 0 || *optarg == '\0' || *e != '\0')
+ errx(1,
+ "illegal timing interval -- %s", optarg);
+ options |= F_INTERVAL;
+ break;
+ case 'l':
+ preload = strtol(optarg, &e, 10);
+ if (preload < 0 || *optarg == '\0' || *e != '\0')
+ errx(1, "illegal preload value -- %s", optarg);
+ break;
+ case 'n':
+ options |= F_NUMERIC;
+ break;
+ case 'p': /* fill buffer with user pattern */
+ options |= F_PINGFILLED;
+ fill((char *)datap, optarg);
+ break;
+ case 'q':
+ options |= F_QUIET;
+ break;
+ case 'R':
+ options |= F_RROUTE;
+ break;
+ case 's': /* size of packet to send */
+ datalen = strtol(optarg, &e, 10);
+ if (datalen <= 0 || *optarg == '\0' || *e != '\0')
+ errx(1, "illegal datalen value -- %s", optarg);
+ if (datalen > MAXDATALEN)
+ errx(1,
+ "datalen value too large, maximum is %d",
+ MAXDATALEN);
+ break;
+ case 'v':
+ options |= F_VERBOSE;
+ break;
+ case 'w':
+ case 'W':
+ options |= F_FQDN;
+ break;
+#ifdef IPSEC
+#ifdef IPSEC_POLICY_IPSEC
+ case 'P':
+ options |= F_POLICY;
+ if (!strncmp("in", optarg, 2))
+ policy_in = strdup(optarg);
+ else if (!strncmp("out", optarg, 3))
+ policy_out = strdup(optarg);
+ else
+ errx(1, "invalid security policy");
+ break;
+#else
+ case 'A':
+ options |= F_AUTHHDR;
+ break;
+ case 'E':
+ options |= F_ENCRYPT;
+ break;
+#endif /*IPSEC_POLICY_IPSEC*/
+#endif /*IPSEC*/
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1)
+ usage();
+
+ if (argc > 1)
+ ip6optlen += inet6_rthdr_space(IPV6_RTHDR_TYPE_0, argc - 1);
+
+ target = argv[argc - 1];
+
+ /* getaddrinfo */
+ bzero(&hints, sizeof(struct addrinfo));
+ if ((options & F_NUMERIC) == 0)
+ hints.ai_flags = AI_CANONNAME;
+ hints.ai_family = AF_INET6;
+ hints.ai_socktype = SOCK_RAW;
+ hints.ai_protocol = IPPROTO_ICMPV6;
+
+ ret_ga = getaddrinfo(target, NULL, &hints, &res);
+ if (ret_ga) {
+ fprintf(stderr, "ping6: %s\n", gai_strerror(ret_ga));
+ exit(1);
+ }
+ if (res->ai_canonname)
+ hostname = res->ai_canonname;
+ else
+ hostname = target;
+
+ if (!res->ai_addr)
+ errx(1, "getaddrinfo failed");
+
+ (void)memcpy(&dst, res->ai_addr, res->ai_addrlen);
+
+ if (options & F_FLOOD && options & F_INTERVAL)
+ errx(1, "-f and -i incompatible options");
+
+ if (datalen >= sizeof(struct timeval)) /* can we time transfer */
+ timing = 1;
+ packlen = datalen + IP6LEN + ICMP6ECHOLEN + EXTRA;
+ if (!(packet = (u_char *)malloc((u_int)packlen)))
+ err(1, "Unable to allocate packet");
+ if (!(options & F_PINGFILLED))
+ for (i = 8; i < datalen; ++i)
+ *datap++ = i;
+
+ ident = getpid() & 0xFFFF;
+
+ if ((s = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) < 0)
+ err(1, "socket");
+
+ hold = 1;
+
+ if (options & F_SO_DEBUG)
+ (void)setsockopt(s, SOL_SOCKET, SO_DEBUG, (char *)&hold,
+ sizeof(hold));
+ optval = IPV6_DEFHLIM;
+ if (IN6_IS_ADDR_MULTICAST(&dst.sin6_addr))
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
+ &optval, sizeof(optval)) == -1)
+ err(1, "IPV6_MULTICAST_HOPS");
+
+#ifdef IPSEC
+#ifdef IPSEC_POLICY_IPSEC
+ if (options & F_POLICY) {
+ if (setpolicy(s, policy_in) < 0)
+ errx(1, ipsec_strerror());
+ if (setpolicy(s, policy_out) < 0)
+ errx(1, ipsec_strerror());
+ }
+#else
+ if (options & F_AUTHHDR) {
+ optval = IPSEC_LEVEL_REQUIRE;
+#ifdef IPV6_AUTH_TRANS_LEVEL
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_AUTH_TRANS_LEVEL,
+ &optval, sizeof(optval)) == -1)
+ err(1, "setsockopt(IPV6_AUTH_TRANS_LEVEL)");
+#else /* old def */
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_AUTH_LEVEL,
+ &optval, sizeof(optval)) == -1)
+ err(1, "setsockopt(IPV6_AUTH_LEVEL)");
+#endif
+ }
+ if (options & F_ENCRYPT) {
+ optval = IPSEC_LEVEL_REQUIRE;
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_ESP_TRANS_LEVEL,
+ &optval, sizeof(optval)) == -1)
+ err(1, "setsockopt(IPV6_ESP_TRANS_LEVEL)");
+ }
+#endif /*IPSEC_POLICY_IPSEC*/
+#endif
+
+#ifdef ICMP6_FILTER
+ {
+ struct icmp6_filter filt;
+ if (!(options & F_VERBOSE)) {
+ ICMP6_FILTER_SETBLOCKALL(&filt);
+ if ((options & F_FQDN) || (options & F_NODEADDR))
+ ICMP6_FILTER_SETPASS(ICMP6_NI_REPLY, &filt);
+ else
+ ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filt);
+ } else {
+ ICMP6_FILTER_SETPASSALL(&filt);
+ }
+ if (setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER, &filt,
+ sizeof(filt)) < 0)
+ err(1, "setsockopt(ICMP6_FILTER)");
+ }
+#endif /*ICMP6_FILTER*/
+
+/*
+ optval = 1;
+ if (IN6_IS_ADDR_MULTICAST(&dst.sin6_addr))
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
+ &optval, sizeof(optval)) == -1)
+ err(1, "IPV6_MULTICAST_LOOP");
+*/
+ /* record route option */
+ if (options & F_RROUTE)
+ errx(1, "record route not available in this implementation");
+
+ /* Outgoing interface */
+#ifndef USE_SIN6_SCOPE_ID
+ if (options & F_INTERFACE)
+ ip6optlen += CMSG_SPACE(sizeof(struct in6_pktinfo));
+#endif
+
+ if (hoplimit != -1)
+ ip6optlen += CMSG_SPACE(sizeof(int));
+
+ /* set IP6 packet options */
+ if (ip6optlen) {
+ if ((scmsg = (char *)malloc(ip6optlen)) == 0)
+ errx(1, "can't allocate enough memory");
+ smsghdr.msg_control = (caddr_t)scmsg;
+ smsghdr.msg_controllen = ip6optlen;
+ scmsgp = (struct cmsghdr *)scmsg;
+ }
+ if (options & F_INTERFACE) {
+#ifndef USE_SIN6_SCOPE_ID
+ struct in6_pktinfo *pktinfo =
+ (struct in6_pktinfo *)(CMSG_DATA(scmsgp));
+
+ if ((pktinfo->ipi6_ifindex = if_nametoindex(ifname)) == 0)
+ errx(1, "%s: invalid interface name", ifname);
+ bzero(&pktinfo->ipi6_addr, sizeof(struct in6_addr));
+ scmsgp->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
+ scmsgp->cmsg_level = IPPROTO_IPV6;
+ scmsgp->cmsg_type = IPV6_PKTINFO;
+
+ scmsgp = CMSG_NXTHDR(&smsghdr, scmsgp);
+#else
+ if ((dst.sin6_scope_id = if_nametoindex(ifname)) == 0)
+ errx(1, "%s: invalid interface name", ifname);
+#endif
+ }
+ if (hoplimit != -1) {
+ scmsgp->cmsg_len = CMSG_LEN(sizeof(int));
+ scmsgp->cmsg_level = IPPROTO_IPV6;
+ scmsgp->cmsg_type = IPV6_HOPLIMIT;
+ *(int *)(CMSG_DATA(scmsgp)) = hoplimit;
+
+ scmsgp = CMSG_NXTHDR(&smsghdr, scmsgp);
+ }
+ if (argc > 1) { /* some intermediate addrs are specified */
+ int hops, error;
+
+ if ((scmsgp = (struct cmsghdr *)inet6_rthdr_init(scmsgp,
+ IPV6_RTHDR_TYPE_0)) == 0)
+ errx(1, "can't initialize rthdr");
+
+ for (hops = 0; hops < argc - 1; hops++) {
+ struct addrinfo *iaip;
+
+ if ((error = getaddrinfo(argv[hops], NULL, &hints, &iaip)))
+ errx(1, gai_strerror(error));
+ if (SIN6(res->ai_addr)->sin6_family != AF_INET6)
+ errx(1,
+ "bad addr family of an intermediate addr");
+
+ if (inet6_rthdr_add(scmsgp,
+ &(SIN6(iaip->ai_addr))->sin6_addr,
+ IPV6_RTHDR_LOOSE))
+ errx(1, "can't add an intermediate node");
+ }
+
+ if (inet6_rthdr_lasthop(scmsgp, IPV6_RTHDR_LOOSE))
+ errx(1, "can't set the last flag");
+
+ scmsgp = CMSG_NXTHDR(&smsghdr, scmsgp);
+ }
+
+ {
+ /*
+ * source selection
+ */
+ int dummy, len = sizeof(src);
+
+ if ((dummy = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
+ err(1, "UDP socket");
+
+ src.sin6_family = AF_INET6;
+ src.sin6_addr = dst.sin6_addr;
+ src.sin6_port = ntohs(DUMMY_PORT);
+ src.sin6_scope_id = dst.sin6_scope_id;
+
+#ifndef USE_SIN6_SCOPE_ID
+ if (setsockopt(dummy, IPPROTO_IPV6, IPV6_PKTOPTIONS,
+ (void *)smsghdr.msg_control,
+ smsghdr.msg_controllen)) {
+ err(1, "UDP setsockopt");
+ }
+#else
+ src.sin6_scope_id = dst.sin6_scope_id;
+#endif
+
+ if (connect(dummy, (struct sockaddr *)&src, len) < 0)
+ err(1, "UDP connect");
+
+ if (getsockname(dummy, (struct sockaddr *)&src, &len) < 0)
+ err(1, "getsockname");
+
+ close(dummy);
+ }
+
+#if defined(SO_SNDBUF) && defined(SO_RCVBUF)
+ if (sockbufsize) {
+ if (datalen > sockbufsize)
+ warnx("you need -b to increae socket buffer size");
+ if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &sockbufsize,
+ sizeof(sockbufsize)) < 0)
+ err(1, "setsockopt(SO_SNDBUF)");
+ if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &sockbufsize,
+ sizeof(sockbufsize)) < 0)
+ err(1, "setsockopt(SO_RCVBUF)");
+ }
+ else {
+ if (datalen > 8 * 1024) /*XXX*/
+ warnx("you need -b to increase socket buffer size");
+ /*
+ * When pinging the broadcast address, you can get a lot of
+ * answers. Doing something so evil is useful if you are trying
+ * to stress the ethernet, or just want to fill the arp cache
+ * to get some stuff for /etc/ethers.
+ */
+ hold = 48 * 1024;
+ setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&hold, sizeof(hold));
+ }
+#endif
+
+ optval = 1;
+#ifndef USE_SIN6_SCOPE_ID
+ setsockopt(s, IPPROTO_IPV6, IPV6_PKTINFO, &optval, sizeof(optval));
+#endif
+ setsockopt(s, IPPROTO_IPV6, IPV6_HOPLIMIT, &optval, sizeof(optval));
+
+ printf("PING6(%d=40+8+%d bytes) ", datalen + 48, datalen);
+ printf("%s --> ", inet_ntop(AF_INET6, &src.sin6_addr, ntop_buf, sizeof(ntop_buf)));
+ printf("%s\n", inet_ntop(AF_INET6, &dst.sin6_addr, ntop_buf, sizeof(ntop_buf)));
+
+ while (preload--) /* Fire off them quickies. */
+ pinger();
+
+ (void)signal(SIGINT, onint);
+ (void)signal(SIGINFO, oninfo);
+
+ if ((options & F_FLOOD) == 0) {
+ (void)signal(SIGALRM, onalrm);
+ itimer.it_interval.tv_sec = interval;
+ itimer.it_interval.tv_usec = 0;
+ itimer.it_value.tv_sec = 0;
+ itimer.it_value.tv_usec = 1;
+ (void)setitimer(ITIMER_REAL, &itimer, NULL);
+ }
+
+ FD_ZERO(&fdset);
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 10000;
+ for (;;) {
+ struct msghdr m;
+ struct cmsghdr *cm;
+ u_char buf[256];
+ struct iovec iov[2];
+
+ if (options & F_FLOOD) {
+ pinger();
+ FD_SET(s, &fdset);
+ if (select(s + 1, &fdset, NULL, NULL, &timeout) < 1)
+ continue;
+ }
+ fromlen = sizeof(from);
+
+ m.msg_name = (caddr_t)&from;
+ m.msg_namelen = sizeof(from);
+ memset(&iov, 0, sizeof(iov));
+ iov[0].iov_base = (caddr_t)packet;
+ iov[0].iov_len = packlen;
+ m.msg_iov = iov;
+ m.msg_iovlen = 1;
+ cm = (struct cmsghdr *)buf;
+ m.msg_control = (caddr_t)buf;
+ m.msg_controllen = sizeof(buf);
+
+ if ((cc = recvmsg(s, &m, 0)) < 0) {
+ if (errno == EINTR)
+ continue;
+ warn("recvfrom");
+ continue;
+ }
+
+ pr_pack(packet, cc, &m);
+ if (npackets && nreceived >= npackets)
+ break;
+ }
+ summary();
+ exit(nreceived == 0);
+}
+
+/*
+ * onalrm --
+ * This routine transmits another ping6.
+ */
+void
+onalrm(signo)
+ int signo;
+{
+ struct itimerval itimer;
+
+ if (!npackets || ntransmitted < npackets) {
+ pinger();
+ return;
+ }
+
+ /*
+ * If we're not transmitting any more packets, change the timer
+ * to wait two round-trip times if we've received any packets or
+ * ten seconds if we haven't.
+ */
+#define MAXWAIT 10
+ if (nreceived) {
+ itimer.it_value.tv_sec = 2 * tmax / 1000;
+ if (itimer.it_value.tv_sec == 0)
+ itimer.it_value.tv_sec = 1;
+ } else
+ itimer.it_value.tv_sec = MAXWAIT;
+ itimer.it_interval.tv_sec = 0;
+ itimer.it_interval.tv_usec = 0;
+ itimer.it_value.tv_usec = 0;
+
+ (void)signal(SIGALRM, onint);
+ (void)setitimer(ITIMER_REAL, &itimer, NULL);
+}
+
+/*
+ * 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.
+ */
+void
+pinger()
+{
+ struct icmp6_hdr *icp;
+ struct iovec iov[2];
+ int i, cc;
+
+ icp = (struct icmp6_hdr *)outpack;
+ icp->icmp6_code = 0;
+ icp->icmp6_cksum = 0;
+ icp->icmp6_seq = ntransmitted++; /* htons later */
+ icp->icmp6_id = htons(ident); /* ID */
+
+ CLR(icp->icmp6_seq % mx_dup_ck);
+ icp->icmp6_seq = htons(icp->icmp6_seq);
+
+ if (options & F_FQDN) {
+ icp->icmp6_type = ICMP6_NI_QUERY;
+ /* code field is always 0 */
+ /* XXX: overwrite icmp6_id */
+ icp->icmp6_data16[0] = htons(NI_QTYPE_FQDN);
+ if (timing)
+ (void)gettimeofday((struct timeval *)
+ &outpack[ICMP6ECHOLEN], NULL);
+ cc = ICMP6_NIQLEN;
+ datalen = 0;
+ } else if (options & F_NODEADDR) {
+ icp->icmp6_type = ICMP6_NI_QUERY;
+ /* code field is always 0 */
+ /* XXX: overwrite icmp6_id */
+ icp->icmp6_data16[0] = htons(NI_QTYPE_NODEADDR);
+ if (timing)
+ (void)gettimeofday((struct timeval *)
+ &outpack[ICMP6ECHOLEN], NULL);
+ cc = ICMP6_NIQLEN;
+ datalen = 0;
+ ((struct icmp6_nodeinfo *)icp)->ni_flags = naflags;
+ }
+ else {
+ icp->icmp6_type = ICMP6_ECHO_REQUEST;
+ if (timing)
+ (void)gettimeofday((struct timeval *)
+ &outpack[ICMP6ECHOLEN], NULL);
+ cc = ICMP6ECHOLEN + datalen;
+ }
+
+ smsghdr.msg_name = (caddr_t)&dst;
+ smsghdr.msg_namelen = sizeof(dst);
+ memset(&iov, 0, sizeof(iov));
+ iov[0].iov_base = (caddr_t)outpack;
+ iov[0].iov_len = cc;
+ smsghdr.msg_iov = iov;
+ smsghdr.msg_iovlen = 1;
+
+ i = sendmsg(s, &smsghdr, 0);
+
+ if (i < 0 || i != cc) {
+ if (i < 0)
+ warn("sendmsg");
+ (void)printf("ping6: wrote %s %d chars, ret=%d\n",
+ hostname, cc, i);
+ }
+ if (!(options & F_QUIET) && options & F_FLOOD)
+ (void)write(STDOUT_FILENO, &DOT, 1);
+}
+
+/*
+ * pr_pack --
+ * 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(buf, cc, mhdr)
+ u_char *buf;
+ int cc;
+ struct msghdr *mhdr;
+{
+ struct icmp6_hdr *icp;
+ int i;
+ int hoplim;
+ struct sockaddr_in6 *from = (struct sockaddr_in6 *)mhdr->msg_name;
+ u_char *cp = NULL, *dp, *end = buf + cc;
+#ifdef OLD_RAWSOCKET
+ struct ip6_hdr *ip;
+#endif
+ struct timeval tv, *tp;
+ double triptime = 0;
+ int dupflag;
+ size_t off;
+
+ (void)gettimeofday(&tv, NULL);
+
+#ifdef OLD_RAWSOCKET
+ /* Check the IP header */
+ ip = (struct ip6_hdr *)buf;
+ if (cc < sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr)) {
+ if (options & F_VERBOSE)
+ warnx("packet too short (%d bytes) from %s\n", cc,
+ inet_ntop(AF_INET6, (void *)&from->sin6_addr,
+ ntop_buf, sizeof(ntop_buf)));
+ return;
+ }
+
+ /* chase nexthdr link */
+ {
+ u_int8_t nh;
+ struct ah *ah;
+ struct ip6_ext *ip6e;
+
+ off = IP6LEN;
+ nh = ip->ip6_nxt;
+ while (nh != IPPROTO_ICMPV6) {
+ if (options & F_VERBOSE)
+ fprintf(stderr, "header chain: type=0x%x\n", nh);
+
+ switch (nh) {
+#ifdef IPSEC
+ case IPPROTO_AH:
+ ah = (struct ah *)(buf + off);
+ off += sizeof(struct ah);
+ off += (ah->ah_len << 2);
+ nh = ah->ah_nxt;
+ break;
+#endif
+
+ case IPPROTO_HOPOPTS:
+ ip6e = (struct ip6_ext *)(buf + off);
+ off += (ip6e->ip6e_len + 1) << 3;
+ nh = ip6e->ip6e_nxt;
+ break;
+ default:
+ if (options & F_VERBOSE) {
+ fprintf(stderr,
+ "unknown header type=0x%x: drop it\n",
+ nh);
+ }
+ return;
+ }
+ }
+ }
+ /* Now the ICMP part */
+ icp = (struct icmp6_hdr *)(buf + off);
+#else
+ if (cc < sizeof(struct icmp6_hdr)) {
+ if (options & F_VERBOSE)
+ warnx("packet too short (%d bytes) from %s\n", cc,
+ inet_ntop(AF_INET6, (void *)&from->sin6_addr,
+ ntop_buf, sizeof(ntop_buf)));
+ return;
+ }
+ icp = (struct icmp6_hdr *)buf;
+ off = 0;
+#endif
+
+ if ((hoplim = get_hoplim(mhdr)) == -1) {
+ warnx("failed to get receiving hop limit");
+ return;
+ }
+
+ if (icp->icmp6_type == ICMP6_ECHO_REPLY) {
+ /* XXX the following line overwrites the original packet */
+ icp->icmp6_seq = ntohs(icp->icmp6_seq);
+ if (ntohs(icp->icmp6_id) != ident)
+ return; /* It was not our ECHO */
+ ++nreceived;
+ if (timing) {
+ tp = (struct timeval *)(icp + 1);
+ tvsub(&tv, tp);
+ triptime = ((double)tv.tv_sec) * 1000.0 +
+ ((double)tv.tv_usec) / 1000.0;
+ tsum += triptime;
+ if (triptime < tmin)
+ tmin = triptime;
+ if (triptime > tmax)
+ tmax = triptime;
+ }
+
+ if (TST(icp->icmp6_seq % mx_dup_ck)) {
+ ++nrepeats;
+ --nreceived;
+ dupflag = 1;
+ } else {
+ SET(icp->icmp6_seq % mx_dup_ck);
+ dupflag = 0;
+ }
+
+ if (options & F_QUIET)
+ return;
+
+ if (options & F_FLOOD)
+ (void)write(STDOUT_FILENO, &BSPACE, 1);
+ else {
+ (void)printf("%d bytes from %s, icmp_seq=%u", cc,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntop_buf, sizeof(ntop_buf)),
+ icp->icmp6_seq);
+ (void)printf(" hlim=%d", hoplim);
+ if (timing)
+ (void)printf(" time=%g ms", triptime);
+ if (dupflag)
+ (void)printf("(DUP!)");
+ /* check the data */
+ cp = buf + off + ICMP6ECHOLEN + ICMP6ECHOTMLEN;
+ dp = outpack + ICMP6ECHOLEN + ICMP6ECHOTMLEN;
+ for (i = 8; cp < end; ++i, ++cp, ++dp) {
+ if (*cp != *dp) {
+ (void)printf("\nwrong data byte #%d should be 0x%x but was 0x%x", i, *dp, *cp);
+ break;
+ }
+ }
+ }
+ } else if (icp->icmp6_type == ICMP6_NI_REPLY) { /* ICMP6_NI_REPLY */
+ struct icmp6_nodeinfo *ni = (struct icmp6_nodeinfo *)(buf + off);
+
+ (void)printf("%d bytes from %s: ", cc,
+ pr_addr(from));
+
+ switch(ntohs(ni->ni_qtype)) {
+ case NI_QTYPE_NOOP:
+ printf("NodeInfo NOOP");
+ break;
+ case NI_QTYPE_SUPTYPES:
+ printf("NodeInfo Supported Qtypes");
+ break;
+ case NI_QTYPE_NODEADDR:
+ pr_nodeaddr(ni, end - (u_char *)ni);
+ break;
+ case NI_QTYPE_FQDN:
+ default: /* XXX: for backward compatibility */
+ cp = (u_char *)ni + ICMP6_NIRLEN + 1;
+ while (cp < end) {
+ if (isprint(*cp))
+ putchar(*cp);
+ else
+ printf("\\%03o", *cp & 0xff);
+ cp++;
+ }
+ if (options & F_VERBOSE) {
+ long ttl;
+
+ (void)printf(" (");
+
+ switch(ni->ni_code) {
+ case ICMP6_NI_REFUSED:
+ (void)printf("refused,");
+ break;
+ case ICMP6_NI_UNKNOWN:
+ (void)printf("unknwon qtype,");
+ break;
+ }
+
+ if ((end - (u_char *)ni) < ICMP6_NIRLEN) {
+ /* case of refusion, unkown */
+ goto fqdnend;
+ }
+ ttl = ntohl(*(u_long *)&buf[off+ICMP6ECHOLEN+8]);
+ if (!(ni->ni_flags & NI_FQDN_FLAG_VALIDTTL))
+ (void)printf("TTL=%d:meaningless",
+ (int)ttl);
+ else {
+ if (ttl < 0)
+ (void)printf("TTL=%d:invalid",
+ (int)ttl);
+ else
+ (void)printf("TTL=%d",
+ (int)ttl);
+ }
+
+ if (buf[off + ICMP6_NIRLEN] !=
+ cc - off - ICMP6_NIRLEN - 1) {
+ (void)printf(",invalid namelen:%d/%lu",
+ buf[off + ICMP6_NIRLEN],
+ (u_long)cc - off - ICMP6_NIRLEN - 1);
+ }
+ putchar(')');
+ }
+ fqdnend:
+ ;
+ }
+ } else {
+ /* We've got something other than an ECHOREPLY */
+ if (!(options & F_VERBOSE))
+ return;
+ (void)printf("%d bytes from %s: ", cc,
+ pr_addr(from));
+ pr_icmph(icp, end);
+ }
+
+ if (!(options & F_FLOOD)) {
+ (void)putchar('\n');
+ (void)fflush(stdout);
+ }
+}
+
+void
+pr_nodeaddr(ni, nilen)
+ struct icmp6_nodeinfo *ni; /* ni->qtype must be NODEADDR */
+ int nilen;
+{
+ struct in6_addr *ia6 = (struct in6_addr *)(ni + 1);
+
+ nilen -= sizeof(struct icmp6_nodeinfo);
+
+ if (options & F_VERBOSE) {
+ switch(ni->ni_code) {
+ case ICMP6_NI_REFUSED:
+ (void)printf("refused");
+ break;
+ case ICMP6_NI_UNKNOWN:
+ (void)printf("unknown qtype");
+ break;
+ }
+ if (ni->ni_flags & NI_NODEADDR_FLAG_ALL)
+ (void)printf(" truncated");
+ }
+ putchar('\n');
+ if (nilen <= 0)
+ printf(" no address\n");
+ for (; nilen > 0; nilen -= sizeof(*ia6), ia6 += 1) {
+ printf(" %s\n",
+ inet_ntop(AF_INET6, ia6, ntop_buf, sizeof(ntop_buf)));
+ }
+}
+
+int
+get_hoplim(mhdr)
+ struct msghdr *mhdr;
+{
+ struct cmsghdr *cm;
+
+ for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(mhdr); cm;
+ cm = (struct cmsghdr *)CMSG_NXTHDR(mhdr, cm)) {
+ if (cm->cmsg_level == IPPROTO_IPV6 &&
+ cm->cmsg_type == IPV6_HOPLIMIT &&
+ cm->cmsg_len == CMSG_LEN(sizeof(int)))
+ return(*(int *)CMSG_DATA(cm));
+ }
+
+ return(-1);
+}
+
+/*
+ * tvsub --
+ * 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;
+}
+
+/*
+ * oninfo --
+ * SIGINFO handler.
+ */
+void
+oninfo(notused)
+ int notused;
+{
+ summary();
+}
+
+/*
+ * onint --
+ * SIGINT handler.
+ */
+void
+onint(notused)
+ int notused;
+{
+ summary();
+
+ (void)signal(SIGINT, SIG_DFL);
+ (void)kill(getpid(), SIGINT);
+
+ /* NOTREACHED */
+ exit(1);
+}
+
+/*
+ * summary --
+ * Print out statistics.
+ */
+void
+summary()
+{
+ register int i;
+
+ (void)printf("\n--- %s ping6 statistics ---\n", hostname);
+ (void)printf("%ld packets transmitted, ", ntransmitted);
+ (void)printf("%ld packets received, ", nreceived);
+ if (nrepeats)
+ (void)printf("+%ld duplicates, ", nrepeats);
+ if (ntransmitted) {
+ if (nreceived > ntransmitted)
+ (void)printf("-- somebody's printing up packets!");
+ else
+ (void)printf("%d%% packet loss",
+ (int) (((ntransmitted - nreceived) * 100) /
+ ntransmitted));
+ }
+ (void)putchar('\n');
+ if (nreceived && timing) {
+ /* Only display average to microseconds */
+ i = 1000.0 * tsum / (nreceived + nrepeats);
+ (void)printf("round-trip min/avg/max = %g/%g/%g ms\n",
+ tmin, ((double)i) / 1000.0, tmax);
+ (void)fflush(stdout);
+ }
+}
+
+#ifdef notdef
+static char *ttab[] = {
+ "Echo Reply", /* ip + seq + udata */
+ "Dest Unreachable", /* net, host, proto, port, frag, sr + IP */
+ "Source Quench", /* IP */
+ "Redirect", /* redirect type, gateway, + IP */
+ "Echo",
+ "Time Exceeded", /* transit, frag reassem + IP */
+ "Parameter Problem", /* pointer + IP */
+ "Timestamp", /* id + seq + three timestamps */
+ "Timestamp Reply", /* " */
+ "Info Request", /* id + sq */
+ "Info Reply" /* " */
+};
+#endif
+
+/*
+ * pr_icmph --
+ * Print a descriptive string about an ICMP header.
+ */
+void
+pr_icmph(icp, end)
+ struct icmp6_hdr *icp;
+ u_char *end;
+{
+ switch(icp->icmp6_type) {
+ case ICMP6_DST_UNREACH:
+ switch(icp->icmp6_code) {
+ case ICMP6_DST_UNREACH_NOROUTE:
+ (void)printf("No Route to Destination\n");
+ break;
+ case ICMP6_DST_UNREACH_ADMIN:
+ (void)printf("Destination Administratively "
+ "Unreachable\n");
+ break;
+ case ICMP6_DST_UNREACH_BEYONDSCOPE:
+ (void)printf("Destination Unreachable Beyond Scope\n");
+ break;
+ case ICMP6_DST_UNREACH_ADDR:
+ (void)printf("Destination Host Unreachable\n");
+ break;
+ case ICMP6_DST_UNREACH_NOPORT:
+ (void)printf("Destination Port Unreachable\n");
+ break;
+ default:
+ (void)printf("Destination Unreachable, Bad Code: %d\n",
+ icp->icmp6_code);
+ break;
+ }
+ /* Print returned IP header information */
+ pr_retip((struct ip6_hdr *)(icp + 1), end);
+ break;
+ case ICMP6_PACKET_TOO_BIG:
+ (void)printf("Packet too big mtu = %d\n",
+ (int)ntohl(icp->icmp6_mtu));
+ break;
+ case ICMP6_TIME_EXCEEDED:
+ switch(icp->icmp6_code) {
+ case ICMP6_TIME_EXCEED_TRANSIT:
+ (void)printf("Time to live exceeded\n");
+ break;
+ case ICMP6_TIME_EXCEED_REASSEMBLY:
+ (void)printf("Frag reassembly time exceeded\n");
+ break;
+ default:
+ (void)printf("Time exceeded, Bad Code: %d\n",
+ icp->icmp6_code);
+ break;
+ }
+ pr_retip((struct ip6_hdr *)(icp + 1), end);
+ break;
+ case ICMP6_PARAM_PROB:
+ (void)printf("Parameter problem: ");
+ switch(icp->icmp6_code) {
+ case ICMP6_PARAMPROB_HEADER:
+ (void)printf("Erroneous Header ");
+ break;
+ case ICMP6_PARAMPROB_NEXTHEADER:
+ (void)printf("Unknown Nextheader ");
+ break;
+ case ICMP6_PARAMPROB_OPTION:
+ (void)printf("Unrecognized Option ");
+ break;
+ default:
+ (void)printf("Bad code(%d) ", icp->icmp6_code);
+ break;
+ }
+ (void)printf("pointer = 0x%02x\n",
+ (int)ntohl(icp->icmp6_pptr));
+ pr_retip((struct ip6_hdr *)(icp + 1), end);
+ break;
+ case ICMP6_ECHO_REQUEST:
+ (void)printf("Echo Request\n");
+ /* XXX ID + Seq + Data */
+ break;
+ case ICMP6_ECHO_REPLY:
+ (void)printf("Echo Reply\n");
+ /* XXX ID + Seq + Data */
+ break;
+ case ICMP6_MEMBERSHIP_QUERY:
+ (void)printf("Membership Query\n");
+ break;
+ case ICMP6_MEMBERSHIP_REPORT:
+ (void)printf("Membership Report\n");
+ break;
+ case ICMP6_MEMBERSHIP_REDUCTION:
+ (void)printf("Membership Reduction\n");
+ break;
+ case ND_ROUTER_SOLICIT:
+ (void)printf("Router Solicitation\n");
+ break;
+ case ND_ROUTER_ADVERT:
+ (void)printf("Router Advertisement\n");
+ break;
+ case ND_NEIGHBOR_SOLICIT:
+ (void)printf("Neighbor Solicitation\n");
+ break;
+ case ND_NEIGHBOR_ADVERT:
+ (void)printf("Neighbor Advertisement\n");
+ break;
+ case ND_REDIRECT:
+ {
+ struct nd_redirect *red = (struct nd_redirect *)icp;
+
+ (void)printf("Redirect\n");
+ (void)printf("Destination: %s\n",
+ inet_ntop(AF_INET6, &red->nd_rd_dst,
+ ntop_buf, sizeof(ntop_buf)));
+ (void)printf("New Target: %s\n",
+ inet_ntop(AF_INET6, &red->nd_rd_target,
+ ntop_buf, sizeof(ntop_buf)));
+ break;
+ }
+ case ICMP6_NI_QUERY:
+ (void)printf("Node Information Query\n");
+ /* XXX ID + Seq + Data */
+ break;
+ case ICMP6_NI_REPLY:
+ (void)printf("Node Information Reply\n");
+ /* XXX ID + Seq + Data */
+ break;
+ default:
+ (void)printf("Bad ICMP type: %d\n", icp->icmp6_type);
+ }
+}
+
+/*
+ * pr_iph --
+ * Print an IP6 header.
+ */
+void
+pr_iph(ip6)
+ struct ip6_hdr *ip6;
+{
+ u_int32_t flow = ip6->ip6_flow & IPV6_FLOWLABEL_MASK;
+ u_int8_t tc;
+
+ tc = *(&ip6->ip6_vfc + 1); /* XXX */
+ tc = (tc >> 4) & 0x0f;
+ tc |= (ip6->ip6_vfc << 4);
+
+ printf("Vr TC Flow Plen Nxt Hlim\n");
+ printf(" %1x %02x %05x %04x %02x %02x\n",
+ (ip6->ip6_vfc & IPV6_VERSION_MASK) >> 4, tc, (int)ntohl(flow),
+ ntohs(ip6->ip6_plen),
+ ip6->ip6_nxt, ip6->ip6_hlim);
+ printf("%s->", inet_ntop(AF_INET6, &ip6->ip6_src,
+ ntop_buf, INET6_ADDRSTRLEN));
+ printf("%s\n", inet_ntop(AF_INET6, &ip6->ip6_dst,
+ ntop_buf, INET6_ADDRSTRLEN));
+}
+
+/*
+ * pr_addr --
+ * Return an ascii host address as a dotted quad and optionally with
+ * a hostname.
+ */
+char *
+pr_addr(addr)
+ struct sockaddr_in6 *addr;
+{
+ static char buf[MAXHOSTNAMELEN];
+ int flag = 0;
+
+ if (options & F_NUMERIC)
+ flag |= NI_NUMERICHOST;
+
+ flag |= NI_WITHSCOPEID;
+
+ getnameinfo((struct sockaddr *)addr, addr->sin6_len, buf, sizeof(buf),
+ NULL, 0, flag);
+
+ return (buf);
+}
+
+/*
+ * pr_retip --
+ * Dump some info on a returned (via ICMPv6) IPv6 packet.
+ */
+void
+pr_retip(ip6, end)
+ struct ip6_hdr *ip6;
+ u_char *end;
+{
+ u_char *cp = (u_char *)ip6, nh;
+ int hlen;
+
+ if (end - (u_char *)ip6 < sizeof(*ip6)) {
+ printf("IP6");
+ goto trunc;
+ }
+ pr_iph(ip6);
+ hlen = sizeof(*ip6);
+
+ nh = ip6->ip6_nxt;
+ cp += hlen;
+ while (end - cp >= 8) {
+ switch (nh) {
+ case IPPROTO_HOPOPTS:
+ printf("HBH ");
+ hlen = (((struct ip6_hbh *)cp)->ip6h_len+1) << 3;
+ nh = ((struct ip6_hbh *)cp)->ip6h_nxt;
+ break;
+ case IPPROTO_DSTOPTS:
+ printf("DSTOPT ");
+ hlen = (((struct ip6_dest *)cp)->ip6d_len+1) << 3;
+ nh = ((struct ip6_dest *)cp)->ip6d_nxt;
+ break;
+ case IPPROTO_FRAGMENT:
+ printf("FRAG ");
+ hlen = sizeof(struct ip6_frag);
+ nh = ((struct ip6_frag *)cp)->ip6f_nxt;
+ break;
+ case IPPROTO_ROUTING:
+ printf("RTHDR ");
+ hlen = (((struct ip6_rthdr *)cp)->ip6r_len+1) << 3;
+ nh = ((struct ip6_rthdr *)cp)->ip6r_nxt;
+ break;
+#ifdef IPSEC
+ case IPPROTO_AH:
+ printf("AH ");
+ hlen = (((struct ah *)cp)->ah_len+2) << 2;
+ nh = ((struct ah *)cp)->ah_nxt;
+ break;
+#endif
+ case IPPROTO_ICMPV6:
+ printf("ICMP6: type = %d, code = %d\n",
+ *cp, *(cp + 1));
+ return;
+ case IPPROTO_ESP:
+ printf("ESP\n");
+ return;
+ case IPPROTO_TCP:
+ printf("TCP: from port %u, to port %u (decimal)\n",
+ (*cp * 256 + *(cp + 1)),
+ (*(cp + 2) * 256 + *(cp + 3)));
+ return;
+ case IPPROTO_UDP:
+ printf("UDP: from port %u, to port %u (decimal)\n",
+ (*cp * 256 + *(cp + 1)),
+ (*(cp + 2) * 256 + *(cp + 3)));
+ return;
+ default:
+ printf("Unknown Header(%d)\n", nh);
+ return;
+ }
+
+ if ((cp += hlen) >= end)
+ goto trunc;
+ }
+ if (end - cp < 8)
+ goto trunc;
+
+ putchar('\n');
+ return;
+
+ trunc:
+ printf("...\n");
+ return;
+}
+
+void
+fill(bp, patp)
+ char *bp, *patp;
+{
+ register int ii, jj, kk;
+ int pat[16];
+ char *cp;
+
+ for (cp = patp; *cp; cp++)
+ if (!isxdigit(*cp))
+ errx(1, "patterns must be specified as hex digits");
+ 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]);
+
+/* xxx */
+ if (ii > 0)
+ for (kk = 0;
+ kk <= MAXDATALEN - (8 + sizeof(struct timeval) + ii);
+ kk += ii)
+ for (jj = 0; jj < ii; ++jj)
+ bp[jj + kk] = pat[jj];
+ if (!(options & F_QUIET)) {
+ (void)printf("PATTERN: 0x");
+ for (jj = 0; jj < ii; ++jj)
+ (void)printf("%02x", bp[jj] & 0xFF);
+ (void)printf("\n");
+ }
+}
+
+#ifdef IPSEC
+#ifdef IPSEC_POLICY_IPSEC
+int
+setpolicy(so, policy)
+ int so;
+ char *policy;
+{
+ char *buf;
+
+ if (policy == NULL)
+ return 0; /* ignore */
+
+ buf = ipsec_set_policy(policy, strlen(policy));
+ if (buf == NULL)
+ errx(1, ipsec_strerror());
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_IPSEC_POLICY,
+ buf, ipsec_get_policylen(buf)) < 0)
+ warnx("Unable to set IPSec policy");
+ free(buf);
+
+ return 0;
+}
+#endif
+#endif
+
+void
+usage()
+{
+ (void)fprintf(stderr,
+"usage: ping6 [-dfnqRrvwW"
+#ifdef IPSEC
+#ifdef IPSEC_POLICY_IPSEC
+ "] [-P policy"
+#else
+ "AE"
+#endif
+#endif
+ "] [-a [alsg]] [-b sockbufsiz] [-c count] [-I interface]\n\
+ [-i wait] [-l preload] [-p pattern] [-s packetsize]\n\
+ [-h hoplimit] host [hosts...]\n");
+ exit(1);
+}
diff --git a/sbin/rtsol/Makefile b/sbin/rtsol/Makefile
new file mode 100644
index 0000000..d373d6c
--- /dev/null
+++ b/sbin/rtsol/Makefile
@@ -0,0 +1,29 @@
+# Copyright (c) 1996 WIDE Project. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modifications, are permitted provided that the above copyright notice
+# and this paragraph are duplicated in all such forms and that any
+# documentation, advertising materials, and other materials related to
+# such distribution and use acknowledge that the software was developed
+# by the WIDE Project, Japan. The name of the Project may not be used to
+# endorse or promote products derived from this software without
+# specific prior written permission. THIS SOFTWARE IS PROVIDED ``AS IS''
+# AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+# LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE.
+# $FreeBSD$
+
+SRCDIR= ${.CURDIR}/../../usr.sbin/rtsold
+
+PROG= rtsol
+SRCS= rtsold.c rtsol.c if.c probe.c dump.c
+
+CFLAGS+=-DINET6
+LDADD+= -lkvm
+DPADD+= ${LIBKVM}
+
+NOMAN= yes
+
+.PATH: ${SRCDIR}
+
+.include <bsd.prog.mk>
diff --git a/sys/netinet6/in6_prefix.c b/sys/netinet6/in6_prefix.c
index 6799d0d..f55e30d 100644
--- a/sys/netinet6/in6_prefix.c
+++ b/sys/netinet6/in6_prefix.c
@@ -777,7 +777,7 @@ init_newprefix(struct in6_rrenumreq *irr, struct ifprefix *ifpr,
return error;
rap->ra_ifid = orap->ra_ifid;
rap->ra_flags.anycast = (orap->ra_addr != NULL &&
- (orap->ra_addr->ia_flags &
+ (orap->ra_addr->ia6_flags &
IN6_IFF_ANYCAST) != 0) ? 1 : 0;
LIST_INSERT_HEAD(&rpp->rp_addrhead, rap, ra_entry);
}
diff --git a/usr.bin/netstat/Makefile b/usr.bin/netstat/Makefile
index ad7d824..792797c 100644
--- a/usr.bin/netstat/Makefile
+++ b/usr.bin/netstat/Makefile
@@ -12,6 +12,6 @@ BINGRP= kmem
BINMODE=2555
DPADD= ${LIBKVM} ${LIBIPX} ${LIBNETGRAPH}
LDADD= -lkvm -lipx -lnetgraph
-CFLAGS+= -DINET6
+CFLAGS+=-DINET6
.include <bsd.prog.mk>
diff --git a/usr.bin/netstat/inet6.c b/usr.bin/netstat/inet6.c
index 154ee32..02c2fbf 100644
--- a/usr.bin/netstat/inet6.c
+++ b/usr.bin/netstat/inet6.c
@@ -390,23 +390,14 @@ ip6_stats(off, name)
printf("\tMbuf statistics:\n");
printf("\t\t%lu one mbuf\n", ip6stat.ip6s_m1);
for (first = 1, i = 0; i < 32; i++) {
+ char ifbuf[IFNAMSIZ];
if (ip6stat.ip6s_m2m[i] != 0) {
if (first) {
printf("\t\ttwo or more mbuf:\n");
first = 0;
}
- printf("\t\t\t"
-#ifdef notyet
- "%s"
-#else
- "if%d"
-#endif
- "= %ld\n",
-#ifdef notyet
+ printf("\t\t\t%s= %ld\n",
if_indextoname(i, ifbuf),
-#else
- i,
-#endif
ip6stat.ip6s_m2m[i]);
}
}
diff --git a/usr.bin/netstat/main.c b/usr.bin/netstat/main.c
index 5bf9759..c0e4381 100644
--- a/usr.bin/netstat/main.c
+++ b/usr.bin/netstat/main.c
@@ -129,11 +129,11 @@ static struct nlist nl[] = {
{ "_ip6stat" },
#define N_ICMP6STAT 29
{ "_icmp6stat" },
-#ifdef notyet
#define N_IPSECSTAT 30
{ "_ipsecstat" },
#define N_IPSEC6STAT 31
{ "_ipsec6stat" },
+#ifdef notyet
#define N_PIM6STAT 32
{ "_pim6stat" },
#define N_MRT6PROTO 33
diff --git a/usr.bin/netstat/route.c b/usr.bin/netstat/route.c
index 1dce615..69fb8e8 100644
--- a/usr.bin/netstat/route.c
+++ b/usr.bin/netstat/route.c
@@ -605,7 +605,7 @@ p_rtentry(rt)
WID_DST);
p_sockaddr(kgetsa(rt->rt_gateway), NULL, RTF_HOST,
#ifdef INET6
- kgetsa(rt->rt_gateway)->sa_family == AF_INET6 ? WID_GW6 :
+ addr.u_sa.sa_family == AF_INET6 ? WID_GW6 :
#endif
WID_GW);
p_flags(rt->rt_flags, "%-6.6s ");
@@ -763,16 +763,17 @@ netname6(sa6, mask)
u_char *p = (u_char *)mask;
u_char *lim;
int masklen, illegal = 0;
-#ifdef notyet
int flag = NI_WITHSCOPEID;
-#endif
if (mask) {
for (masklen = 0, lim = p + 16; p < lim; p++) {
+ if (*p == 0xff)
+ masklen += 8;
+ else
+ break;
+ }
+ if (p < lim) {
switch (*p) {
- case 0xff:
- masklen += 8;
- break;
case 0xfe:
masklen += 7;
break;
@@ -810,14 +811,10 @@ netname6(sa6, mask)
if (masklen == 0 && IN6_IS_ADDR_UNSPECIFIED(&sa6->sin6_addr))
return("default");
-#ifdef notyet
if (nflag)
flag |= NI_NUMERICHOST;
getnameinfo((struct sockaddr *)sa6, sa6->sin6_len, line, sizeof(line),
NULL, 0, flag);
-#else
- inet_ntop(AF_INET6, (void *)&sa6->sin6_addr, line, sizeof(line));
-#endif
if (nflag)
sprintf(&line[strlen(line)], "/%d", masklen);
@@ -829,37 +826,14 @@ char *
routename6(sa6)
struct sockaddr_in6 *sa6;
{
-#ifdef notyet
+ static char line[MAXHOSTNAMELEN + 1];
int flag = NI_WITHSCOPEID;
if (nflag)
flag |= NI_NUMERICHOST;
-#else
- register char *cp;
-#endif
- static char line[MAXHOSTNAMELEN + 1];
- struct hostent *hp;
-#ifdef notyet
getnameinfo((struct sockaddr *)sa6, sa6->sin6_len, line, sizeof(line),
NULL, 0, flag);
-#else
- cp = 0;
- if (!nflag) {
- hp = gethostbyaddr((char *)&sa6->sin6_addr,
- sizeof(sa6->sin6_addr), AF_INET6);
- if (hp) {
- cp = hp->h_name;
- trimdomain(cp);
- }
- }
- if (cp) {
- strncpy(line, cp, sizeof(line) - 1);
- line[sizeof(line) - 1] = '\0';
- } else
- inet_ntop(AF_INET6, (void *)&sa6->sin6_addr, line,
- sizeof(line));
-#endif
return line;
}
diff --git a/usr.sbin/gifconfig/Makefile b/usr.sbin/gifconfig/Makefile
new file mode 100644
index 0000000..b562390
--- /dev/null
+++ b/usr.sbin/gifconfig/Makefile
@@ -0,0 +1,21 @@
+# Copyright (c) 1996 WIDE Project. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modifications, are permitted provided that the above copyright notice
+# and this paragraph are duplicated in all such forms and that any
+# documentation, advertising materials, and other materials related to
+# such distribution and use acknowledge that the software was developed
+# by the WIDE Project, Japan. The name of the Project may not be used to
+# endorse or promote products derived from this software without
+# specific prior written permission. THIS SOFTWARE IS PROVIDED ``AS IS''
+# AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+# LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE.
+# $FreeBSD$
+
+PROG= gifconfig
+MAN8= gifconfig.8
+
+CFLAGS+=-DINET6
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/gifconfig/gifconfig.8 b/usr.sbin/gifconfig/gifconfig.8
new file mode 100644
index 0000000..702777f
--- /dev/null
+++ b/usr.sbin/gifconfig/gifconfig.8
@@ -0,0 +1,140 @@
+.\" 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.
+.\"
+.\" $Id: gifconfig.8,v 1.2 1999/10/07 04:25:54 itojun Exp $
+.\" $FreeBSD$
+.\"
+.Dd May 17, 1998
+.Dt GIFCONFIG 8
+.Os KAME
+.\"
+.Sh NAME
+.Nm gifconfig
+.Nd configure generic IP tunnel
+.\"
+.Sh SYNOPSIS
+.Nm
+.Ar interface
+.Op Ar af
+.Op Ar physsrc physdest
+.Nm gifconfig
+.Fl a
+.\"
+.Sh DESCRIPTION
+.Nm
+configures the physical address for the generic IP tunnel
+inteface, such as "gif0".
+Argument
+.Ar physsrc
+and
+.Ar physdest
+are interpreted as the outer source/destination address for
+encapsulating IPv4/v6 header.
+Argument
+.Ar af
+specifies the address family for
+.Ar physsrc
+and
+.Ar physdest .
+.Ar Af
+can be
+.Li inet
+or
+.Li inet6 ,
+and will be treated as
+.Li inet
+if ommitted.
+.Pp
+.Nm
+takes the following optional argument:
+.Bl -tag -width Ds
+.It Fl a
+Display information associated all generic IP tunnel interfaces.
+.El
+.Pp
+Please note that it is very easy to create infinite routing loop,
+when you configure tunnel over same address family
+.Po
+e.g. IPv4-over-IPv4
+.Pc .
+.\"
+.Sh EXAMPLES
+If you would like to configure IPv6 over IPv4
+.Pq aka IPv6 in IPv4
+tunnel between
+.Li 10.1.1.1
+and
+.Li 10.2.3.4 ,
+you should perform the following command:
+.Bd -literal -offset
+# gifconfig gif0 inet 10.1.1.1 10.2.3.4
+.Ed
+.Pp
+.\" To use the
+.\" .Li 0.0.0.0
+.\" feature to establish a tunnel from host1 to host3
+.\" which will encapsulate and carry packets from host2, on host1 do:
+.\" .Bd -literal -offset
+.\" # ifconfig gif0 inet host1 127.0.0.2 # assign an address to gif0
+.\" # gifconfig gif0 inet host1 0.0.0.0 # assign encapsulation addresses
+.\" # route add host2 host3 -ifp gif0: # encap host2 packets, send to host3
+.\" .Ed
+.\" .Pp
+.\" Note: the
+.\" .Fl ifp
+.\" option to route does not work as documented in
+.\" most versions of FreeBSD.
+.\" .Pp
+.\" On host3 do:
+.\" .Bd -literal -offset
+.\" # ifconfig gif0 inet host3 127.0.0.2 # assign an address to gif0
+.\" # gifconfig gif0 inet host3 0.0.0.0 # assign encapsulation addresses
+.\" .Ed
+.\" .Pp
+.\" Now if you ping host2 from host1, the packets should be encapsulated
+.\" with outer source address = host1 and outer destination address = host3,
+.\" and delivered to host3.
+.\" host3 will decapsulate the packet and deliver it normally to host2.
+.\" .Pp
+This is also possible to use IPv6 as outer proto, by replacing
+.Li inet
+to
+.Li inet6 ,
+and IPv4 addresses to some appropriate IPv6 addresses in above example.
+.\"
+.Sh RETURN VALUES
+The command exits with exit status of 1 on errors, 0 on success.
+.\"
+.Sh SEE ALSO
+.Xr gif 4 ,
+.Xr ifconfig 8
+.Sh HISTORY
+The
+.Nm
+command first appeared in WIDE Hydrangea IPv6 protocol stack kit.
+.\" .Sh BUGS
+.\" (to be written)
diff --git a/usr.sbin/gifconfig/gifconfig.c b/usr.sbin/gifconfig/gifconfig.c
new file mode 100644
index 0000000..3f36b03
--- /dev/null
+++ b/usr.sbin/gifconfig/gifconfig.c
@@ -0,0 +1,834 @@
+/*
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * gifconfig, derived from ifconfig
+ *
+ * @(#) Copyright (c) 1983, 1993\n\
+ * The Regents of the University of California. All rights reserved.\n
+ *
+ * @(#)ifconfig.c 8.2 (Berkeley) 2/16/94
+ */
+
+/*
+ * 951109 - Andrew@pubnix.net - Changed to iterative buffer growing mechanism
+ * for ifconfig -a so all interfaces are queried.
+ *
+ * 960101 - peter@freebsd.org - Blow away the SIOCGIFCONF code and use
+ * sysctl() to get the structured interface conf
+ * and parse the messages in there. REALLY UGLY!
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+#include <net/if_var.h>
+#endif /* __FreeBSD__ >= 3 */
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <sys/protosw.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <nlist.h>
+#include <kvm.h>
+#include <fcntl.h>
+
+struct ifreq ifr;
+struct ifaliasreq addreq;
+#ifdef INET6
+struct in6_ifreq in6_ifr;
+struct in6_aliasreq in6_addreq;
+#endif
+
+char name[32];
+int flags;
+int metric;
+int mtu;
+int setpsrc = 0;
+int s;
+kvm_t *kvmd;
+extern int errno;
+
+#ifdef INET6
+char ntop_buf[INET6_ADDRSTRLEN]; /*inet_ntop()*/
+#endif
+
+void setifpsrc __P((char *, int));
+void setifpdst __P((char *, int));
+void setifflags __P((char *, int));
+
+
+#define NEXTARG 0xffffff
+
+struct cmd {
+ char *c_name;
+ int c_parameter; /* NEXTARG means next argv */
+ void (*c_func) __P((char *, int));
+} cmds[] = {
+ { "up", IFF_UP, setifflags } ,
+ { "down", -IFF_UP, setifflags },
+ { 0, 0, setifpsrc },
+ { 0, 0, setifpdst },
+};
+
+/*
+ * XNS support liberally adapted from code written at the University of
+ * Maryland principally by James O'Toole and Chris Torek.
+ */
+int main __P((int, char *[]));
+void status __P((void));
+void phys_status __P((int));
+void in_status __P((int));
+#ifdef INET6
+void in6_status __P((int));
+#endif
+void ether_status __P((int));
+void Perror __P((char *));
+void in_getaddr __P((char *, int));
+#ifdef INET6
+void in6_getaddr __P((char *, int));
+void in6_getprefix __P((char *, int));
+#endif
+void printb __P((char *, unsigned int, char *));
+int prefix __P((void *, int));
+
+char ntop_buf[INET6_ADDRSTRLEN];
+
+/* Known address families */
+struct afswtch {
+ char *af_name;
+ short af_af;
+ void (*af_status) __P((int));
+ void (*af_getaddr) __P((char *, int));
+ void (*af_getprefix) __P((char *, int));
+ u_long af_pifaddr;
+ caddr_t af_addreq;
+ caddr_t af_req;
+} afs[] = {
+#define C(x) ((caddr_t) &x)
+ { "inet", AF_INET, in_status, in_getaddr, 0,
+ SIOCSIFPHYADDR, C(addreq), C(ifr) },
+#ifdef INET6
+ { "inet6", AF_INET6, in6_status, in6_getaddr, in6_getprefix,
+ SIOCSIFPHYADDR_IN6, C(in6_addreq), C(in6_ifr) },
+#endif
+ { "ether", AF_INET, ether_status, NULL, NULL }, /* XXX not real!! */
+ { 0, 0, 0, 0, 0 }
+};
+
+struct afswtch *afp = NULL; /*the address family being set or asked about*/
+
+void rt_xaddrs __P((caddr_t, caddr_t, struct rt_addrinfo *));
+int ifconfig __P((int argc, char *argv[], int af, struct afswtch *rafp));
+
+
+
+/*
+ * Expand the compacted form of addresses as returned via the
+ * configuration read via sysctl().
+ */
+
+#define ROUNDUP(a) \
+ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
+#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
+
+void
+rt_xaddrs(cp, cplim, rtinfo)
+ caddr_t cp, cplim;
+ struct rt_addrinfo *rtinfo;
+{
+ struct sockaddr *sa;
+ int i;
+
+ memset(rtinfo->rti_info, 0, sizeof(rtinfo->rti_info));
+ for (i = 0; (i < RTAX_MAX) && (cp < cplim); i++) {
+ if ((rtinfo->rti_addrs & (1 << i)) == 0)
+ continue;
+ rtinfo->rti_info[i] = sa = (struct sockaddr *)cp;
+ ADVANCE(cp, sa);
+ }
+}
+
+
+/*
+ * Grunge for new-style sysctl() decoding.. :-(
+ * Apologies to the world for committing gross things like this in 1996..
+ */
+struct if_msghdr *ifm;
+struct ifa_msghdr *ifam;
+struct sockaddr_dl *sdl;
+struct rt_addrinfo info;
+char *buf, *lim, *next;
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int af = AF_INET;
+ struct afswtch *rafp = NULL;
+ size_t needed;
+ int mib[6];
+ int all;
+
+ if (argc < 2) {
+ fprintf(stderr, "usage: gifconfig interface %s",
+ "[ af ] physsrc physdst\n");
+ exit(1);
+ }
+ argc--, argv++;
+ strncpy(name, *argv, sizeof(name));
+ strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
+ argc--, argv++;
+ if (argc > 0) {
+ for (afp = rafp = afs; rafp->af_name; rafp++)
+ if (strcmp(rafp->af_name, *argv) == 0) {
+ afp = rafp; argc--; argv++;
+ break;
+ }
+ rafp = afp;
+ af = ifr.ifr_addr.sa_family = rafp->af_af;
+ }
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = 0; /* address family */
+ mib[4] = NET_RT_IFLIST;
+ mib[5] = 0;
+
+ /* if particular family specified, only ask about it */
+ if (afp) {
+ mib[3] = afp->af_af;
+ }
+
+ if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
+ errx(1, "iflist-sysctl-estimate");
+ if ((buf = malloc(needed)) == NULL)
+ errx(1, "malloc");
+ if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
+ errx(1, "actual retrieval of interface table");
+ lim = buf + needed;
+
+ all = 0;
+ if (strcmp(name, "-a") == 0)
+ all = 1; /* All interfaces */
+ else if (strcmp(name, "-au") == 0)
+ all = 2; /* All IFF_UPinterfaces */
+ else if (strcmp(name, "-ad") == 0)
+ all = 3; /* All !IFF_UP interfaces */
+
+ for (next = buf; next < lim; next += ifm->ifm_msglen) {
+
+ ifm = (struct if_msghdr *)next;
+
+ /* XXX: Swallow up leftover NEWADDR messages */
+ if (ifm->ifm_type == RTM_NEWADDR)
+ continue;
+
+ if (ifm->ifm_type == RTM_IFINFO) {
+ sdl = (struct sockaddr_dl *)(ifm + 1);
+ flags = ifm->ifm_flags;
+ } else {
+ errx(1, "out of sync parsing NET_RT_IFLIST");
+ }
+
+ switch(all) {
+ case -1:
+ case 0:
+ if (strlen(name) != sdl->sdl_nlen)
+ continue; /* not same len */
+ if (strncmp(name, sdl->sdl_data, sdl->sdl_nlen) != 0)
+ continue; /* not same name */
+ break;
+ case 1:
+ break; /* always do it */
+ case 2:
+ if ((flags & IFF_UP) == 0)
+ continue; /* not up */
+ break;
+ case 3:
+ if (flags & IFF_UP)
+ continue; /* not down */
+ break;
+ }
+
+ /*
+ * Let's just do it for gif only
+ */
+ if (sdl->sdl_type != IFT_GIF) {
+ if (all != 0)
+ continue;
+
+ fprintf(stderr, "gifconfig: %s is not gif.\n",
+ ifr.ifr_name);
+ exit(1);
+ }
+
+ if (all > 0) {
+ strncpy(name, sdl->sdl_data, sdl->sdl_nlen);
+ name[sdl->sdl_nlen] = '\0';
+ }
+
+ if ((s = socket(af, SOCK_DGRAM, 0)) < 0) {
+ perror("gifconfig: socket");
+ exit(1);
+ }
+
+ ifconfig(argc,argv,af,rafp);
+
+ close(s);
+
+ if (all == 0) {
+ all = -1; /* flag it as 'done' */
+ break;
+ }
+ }
+ free(buf);
+
+ if (all == 0)
+ errx(1, "interface %s does not exist", name);
+
+
+ exit (0);
+}
+
+
+int
+ifconfig(argc, argv, af, rafp)
+ int argc;
+ char *argv[];
+ int af;
+ struct afswtch *rafp;
+{
+
+ af = 0; /*fool gcc*/
+
+ strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
+#ifdef INET6
+ strncpy(in6_ifr.ifr_name, name, sizeof in6_ifr.ifr_name);
+#endif /* INET6 */
+
+ if (ioctl(s, SIOCGIFMETRIC, (caddr_t)&ifr) < 0)
+ perror("ioctl (SIOCGIFMETRIC)");
+ else
+ metric = ifr.ifr_metric;
+
+#if defined(SIOCGIFMTU) && !defined(__OpenBSD__)
+ if (ioctl(s, SIOCGIFMTU, (caddr_t)&ifr) < 0)
+ perror("ioctl (SIOCGIFMTU)");
+ else
+ mtu = ifr.ifr_mtu;
+#else
+ mtu = 0;
+#endif
+
+ if (argc == 0) {
+ status();
+ return(0);
+ }
+
+ while (argc > 0) {
+ register struct cmd *p;
+
+ for (p = cmds; p->c_name; p++)
+ if (strcmp(*argv, p->c_name) == 0)
+ break;
+ if (p->c_name == 0 && setpsrc)
+ p++; /* got src, do dst */
+ if (p->c_func) {
+ if (p->c_parameter == NEXTARG) {
+ if (argv[1] == NULL)
+ errx(1, "'%s' requires argument",
+ p->c_name);
+ (*p->c_func)(argv[1], 0);
+ argc--, argv++;
+ } else
+ (*p->c_func)(*argv, p->c_parameter);
+ }
+ argc--, argv++;
+ }
+ if (1 /*newaddr*/) {
+ strncpy(rafp->af_addreq, name, sizeof ifr.ifr_name);
+ if (ioctl(s, rafp->af_pifaddr, rafp->af_addreq) < 0)
+ Perror("ioctl (SIOCSIFPHYADDR)");
+ }
+ return(0);
+}
+#define PSRC 0
+#define PDST 1
+
+/*ARGSUSED*/
+void
+setifpsrc(addr, param)
+ char *addr;
+ int param;
+{
+ param = 0; /*fool gcc*/
+ (*afp->af_getaddr)(addr, PSRC);
+ setpsrc = 1;
+}
+
+/*ARGSUSED*/
+void
+setifpdst(addr, param)
+ char *addr;
+ int param;
+{
+ param = 0; /*fool gcc*/
+ (*afp->af_getaddr)(addr, PDST);
+}
+
+void
+setifflags(vname, value)
+ char *vname;
+ int value;
+{
+ if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
+ Perror("ioctl (SIOCGIFFLAGS)");
+ exit(1);
+ }
+ strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name));
+ flags = ifr.ifr_flags;
+
+ if (value < 0) {
+ value = -value;
+ flags &= ~value;
+ } else
+ flags |= value;
+ ifr.ifr_flags = flags;
+ if (ioctl(s, SIOCSIFFLAGS, (caddr_t)&ifr) < 0)
+ Perror(vname);
+}
+
+#define IFFBITS \
+"\020\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5POINTOPOINT\6NOTRAILERS\7RUNNING\10NOARP\
+\11PROMISC\12ALLMULTI\13OACTIVE\14SIMPLEX\15LINK0\16LINK1\17LINK2\20MULTICAST"
+
+/*
+ * Print the status of the interface. If an address family was
+ * specified, show it and it only; otherwise, show them all.
+ */
+void
+status()
+{
+ struct afswtch *p = NULL;
+ char *mynext;
+ struct if_msghdr *myifm;
+
+ printf("%s: ", name);
+ printb("flags", flags, IFFBITS);
+ if (metric)
+ printf(" metric %d", metric);
+ if (mtu)
+ printf(" mtu %d", mtu);
+ putchar('\n');
+
+ /*
+ * XXX: Sigh. This is bad, I know. At this point, we may have
+ * *zero* RTM_NEWADDR's, so we have to "feel the water" before
+ * incrementing the loop. One day, I might feel inspired enough
+ * to get the top level loop to pass a count down here so we
+ * dont have to mess with this. -Peter
+ */
+ myifm = ifm;
+
+ while (1) {
+
+ mynext = next + ifm->ifm_msglen;
+
+ if (mynext >= lim)
+ break;
+
+ myifm = (struct if_msghdr *)mynext;
+
+ if (myifm->ifm_type != RTM_NEWADDR)
+ break;
+
+ next = mynext;
+
+ ifm = (struct if_msghdr *)next;
+
+ ifam = (struct ifa_msghdr *)myifm;
+ info.rti_addrs = ifam->ifam_addrs;
+
+ /* Expand the compacted addresses */
+ rt_xaddrs((char *)(ifam + 1), ifam->ifam_msglen + (char *)ifam,
+ &info);
+
+ if (afp) {
+ if (afp->af_af == info.rti_info[RTAX_IFA]->sa_family &&
+ afp->af_status != ether_status) {
+ p = afp;
+ if (p->af_status != ether_status)
+ (*p->af_status)(1);
+ }
+ } else for (p = afs; p->af_name; p++) {
+ if (p->af_af == info.rti_info[RTAX_IFA]->sa_family &&
+ p->af_status != ether_status)
+ (*p->af_status)(0);
+ }
+ }
+ if (afp == NULL || afp->af_status == ether_status)
+ ether_status(0);
+ else if (afp && !p) {
+ warnx("%s has no %s IFA address!", name, afp->af_name);
+ }
+
+ phys_status(0);
+}
+
+void
+phys_status(force)
+ int force;
+{
+ char psrcaddr[256];
+ char pdstaddr[256];
+ char hostname[NI_MAXHOST];
+ u_long srccmd, dstcmd;
+ int flags = NI_NUMERICHOST;
+ struct ifreq *ifrp;
+ char *ver = "";
+
+ force = 0; /*fool gcc*/
+
+ psrcaddr[0] = pdstaddr[0] = '\0';
+
+#ifdef INET6
+ srccmd = SIOCGIFPSRCADDR_IN6;
+ dstcmd = SIOCGIFPDSTADDR_IN6;
+ ifrp = (struct ifreq *)&in6_ifr;
+#else /* INET6 */
+ ifrp = ifr;
+ srccmd = SIOCGIFPSRCADDR;
+ dstcmd = SIOCGIFPDSTADDR;
+#endif /* INET6 */
+
+ if (0 <= ioctl(s, srccmd, (caddr_t)ifrp)) {
+ getnameinfo(&ifrp->ifr_addr, ifrp->ifr_addr.sa_len,
+ hostname, NI_MAXHOST, 0, 0, flags);
+#ifdef INET6
+ if (ifrp->ifr_addr.sa_family == AF_INET6)
+ ver = "6";
+#endif /* INET6 */
+ sprintf(psrcaddr, "inet%s %s", ver, hostname);
+ }
+ if (0 <= ioctl(s, dstcmd, (caddr_t)ifrp)) {
+ getnameinfo(&ifrp->ifr_addr, ifrp->ifr_addr.sa_len,
+ hostname, NI_MAXHOST, 0, 0, flags);
+ sprintf(pdstaddr, "%s", hostname);
+ }
+ printf("\tphysical address %s --> %s\n", psrcaddr, pdstaddr);
+}
+
+void
+in_status(force)
+ int force;
+{
+ struct sockaddr_in *sin, null_sin;
+#if 0
+ char *inet_ntoa();
+#endif
+
+ memset(&null_sin, 0, sizeof(null_sin));
+
+ sin = (struct sockaddr_in *)info.rti_info[RTAX_IFA];
+ if (!sin || sin->sin_family != AF_INET) {
+ if (!force)
+ return;
+ /* warnx("%s has no AF_INET IFA address!", name); */
+ sin = &null_sin;
+ }
+ printf("\tinet %s ", inet_ntoa(sin->sin_addr));
+
+ if (flags & IFF_POINTOPOINT) {
+ /* note RTAX_BRD overlap with IFF_BROADCAST */
+ sin = (struct sockaddr_in *)info.rti_info[RTAX_BRD];
+ if (!sin)
+ sin = &null_sin;
+ printf("--> %s ", inet_ntoa(sin->sin_addr));
+ }
+
+ sin = (struct sockaddr_in *)info.rti_info[RTAX_NETMASK];
+ if (!sin)
+ sin = &null_sin;
+ printf("netmask 0x%x ", (u_int32_t)ntohl(sin->sin_addr.s_addr));
+
+ if (flags & IFF_BROADCAST) {
+ /* note RTAX_BRD overlap with IFF_POINTOPOINT */
+ sin = (struct sockaddr_in *)info.rti_info[RTAX_BRD];
+ if (sin && sin->sin_addr.s_addr != 0)
+ printf("broadcast %s", inet_ntoa(sin->sin_addr));
+ }
+ putchar('\n');
+}
+
+#ifdef INET6
+void
+in6_status(force)
+ int force;
+{
+ struct sockaddr_in6 *sin, null_sin;
+#if 0
+ char *inet_ntop();
+#endif
+
+
+ memset(&null_sin, 0, sizeof(null_sin));
+
+ sin = (struct sockaddr_in6 *)info.rti_info[RTAX_IFA];
+ if (!sin || sin->sin6_family != AF_INET6) {
+ if (!force)
+ return;
+ /* warnx("%s has no AF_INET6 IFA address!", name); */
+ sin = &null_sin;
+ }
+ printf("\tinet6 %s ", inet_ntop(AF_INET6, &sin->sin6_addr,
+ ntop_buf, sizeof(ntop_buf)));
+
+ if (flags & IFF_POINTOPOINT) {
+ /* note RTAX_BRD overlap with IFF_BROADCAST */
+ sin = (struct sockaddr_in6 *)info.rti_info[RTAX_BRD];
+ /*
+ * some of ther interfaces do not have valid destination
+ * address.
+ */
+ if (sin->sin6_family == AF_INET6) {
+ if (!sin)
+ sin = &null_sin;
+ printf("--> %s ", inet_ntop(AF_INET6, &sin->sin6_addr,
+ ntop_buf, sizeof(ntop_buf)));
+ }
+ }
+
+ sin = (struct sockaddr_in6 *)info.rti_info[RTAX_NETMASK];
+ if (!sin)
+ sin = &null_sin;
+ printf(" prefixlen %d ", prefix(&sin->sin6_addr,
+ sizeof(struct in6_addr)));
+
+ putchar('\n');
+}
+#endif /*INET6*/
+
+/*ARGSUSED*/
+void
+ether_status(dummy)
+ int dummy;
+{
+ char *cp;
+ int n;
+
+ dummy = 0; /*fool gcc*/
+
+ cp = (char *)LLADDR(sdl);
+ if ((n = sdl->sdl_alen) > 0) {
+ if (sdl->sdl_type == IFT_ETHER)
+ printf ("\tether ");
+ else
+ printf ("\tlladdr ");
+ while (--n >= 0)
+ printf("%02x%c",*cp++ & 0xff, n>0? ':' : ' ');
+ putchar('\n');
+ }
+}
+
+void
+Perror(cmd)
+ char *cmd;
+{
+ extern int errno;
+
+ switch (errno) {
+
+ case ENXIO:
+ errx(1, "%s: no such interface", cmd);
+ break;
+
+ case EPERM:
+ errx(1, "%s: permission denied", cmd);
+ break;
+
+ default:
+ err(1, "%s", cmd);
+ }
+}
+
+#define SIN(x) ((struct sockaddr_in *) &(x))
+struct sockaddr_in *sintab[] = {
+SIN(addreq.ifra_addr), SIN(addreq.ifra_dstaddr)};
+
+void
+in_getaddr(s, which)
+ char *s;
+ int which;
+{
+ register struct sockaddr_in *sin = sintab[which];
+ struct hostent *hp;
+ struct netent *np;
+
+ sin->sin_len = sizeof(*sin);
+ sin->sin_family = AF_INET;
+
+ if (inet_aton(s, &sin->sin_addr))
+ ;
+ else if ((hp = gethostbyname(s)) != NULL)
+ bcopy(hp->h_addr, (char *)&sin->sin_addr, hp->h_length);
+ else if ((np = getnetbyname(s)) != NULL)
+ sin->sin_addr = inet_makeaddr(np->n_net, INADDR_ANY);
+ else
+ errx(1, "%s: bad value", s);
+}
+
+#ifdef INET6
+#define SIN6(x) ((struct sockaddr_in6 *) &(x))
+struct sockaddr_in6 *sin6tab[] = {
+SIN6(in6_addreq.ifra_addr), SIN6(in6_addreq.ifra_dstaddr)};
+
+void
+in6_getaddr(s, which)
+ char *s;
+ int which;
+{
+ register struct sockaddr_in6 *sin = sin6tab[which];
+
+ sin->sin6_len = sizeof(*sin);
+ sin->sin6_family = AF_INET6;
+
+ if (inet_pton(AF_INET6, s, &sin->sin6_addr) != 1)
+ errx(1, "%s: bad value", s);
+}
+
+void
+in6_getprefix(plen, which)
+ char *plen;
+ int which;
+{
+ register struct sockaddr_in6 *sin = sin6tab[which];
+ register u_char *cp;
+ int len = atoi(plen);
+
+ if ((len < 0) || (len > 128))
+ errx(1, "%s: bad value", plen);
+ sin->sin6_len = sizeof(*sin);
+ sin->sin6_family = AF_INET6;
+ if ((len == 0) || (len == 128)) {
+ memset(&sin->sin6_addr, -1, sizeof(struct in6_addr));
+ return;
+ }
+ for (cp = (u_char *)&sin->sin6_addr; len > 7; len -= 8)
+ *cp++ = -1;
+ *cp = (-1) << (8 - len);
+}
+#endif
+
+/*
+ * Print a value a la the %b format of the kernel's printf
+ */
+void
+printb(s, v, bits)
+ char *s;
+ register unsigned int v;
+ register char *bits;
+{
+ register int i, any = 0;
+ register char c;
+
+ if (bits && *bits == 8)
+ printf("%s=%o", s, v & 0xffff);
+ else
+ printf("%s=%x", s, v & 0xffff);
+ bits++;
+ if (bits) {
+ putchar('<');
+ while ((i = *bits++) != 0) {
+ if ((v & (1 << (i-1))) != 0) {
+ if (any)
+ putchar(',');
+ any = 1;
+ for (; (c = *bits) > 32; bits++)
+ putchar(c);
+ } else
+ for (; *bits > 32; bits++)
+ ;
+ }
+ putchar('>');
+ }
+}
+
+#ifdef INET6
+int
+prefix(val, size)
+ void *val;
+ int size;
+{
+ register u_char *name = (u_char *)val;
+ register int byte, bit, plen = 0;
+
+ for (byte = 0; byte < size; byte++, plen += 8)
+ if (name[byte] != 0xff)
+ break;
+ if (byte == size)
+ return (plen);
+ for (bit = 7; bit != 0; bit--, plen++)
+ if (!(name[byte] & (1 << bit)))
+ break;
+ for (; bit != 0; bit--)
+ if (name[byte] & (1 << bit))
+ return(0);
+ byte++;
+ for (; byte < size; byte++)
+ if (name[byte])
+ return(0);
+ return (plen);
+}
+#endif /*INET6*/
diff --git a/usr.sbin/ifmcstat/Makefile b/usr.sbin/ifmcstat/Makefile
new file mode 100644
index 0000000..1cebcca
--- /dev/null
+++ b/usr.sbin/ifmcstat/Makefile
@@ -0,0 +1,14 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+# $FreeBSD$
+
+PROG= ifmcstat
+MAN8= ifmcstat.8
+
+BINGRP= kmem
+BINMODE=2555
+LDADD= -lkvm
+DPADD= ${LIBKVM}
+
+CFLAGS+=-DINET6
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ifmcstat/ifmcstat.8 b/usr.sbin/ifmcstat/ifmcstat.8
new file mode 100644
index 0000000..7f9d047
--- /dev/null
+++ b/usr.sbin/ifmcstat/ifmcstat.8
@@ -0,0 +1,34 @@
+.\" Copyright (c) 1996 WIDE Project. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modifications, are permitted provided that the above copyright notice
+.\" and this paragraph are duplicated in all such forms and that any
+.\" documentation, advertising materials, and other materials related to
+.\" such distribution and use acknowledge that the software was developed
+.\" by the WIDE Project, Japan. The name of the Project may not be used to
+.\" endorse or promote products derived from this software without
+.\" specific prior written permission. THIS SOFTWARE IS PROVIDED ``AS IS''
+.\" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+.\" LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+.\" A PARTICULAR PURPOSE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd May 21, 1998
+.Dt IFMCSTAT 8
+.Os KAME
+.Sh NAME
+.Nm ifmcstat
+.Nd dump multicast group management statistics per interface
+.Sh SYNOPSIS
+.Nm
+.\"
+.Sh DESCRIPTION
+The
+.Nm Ifmcstat
+dumps multicast group information in the kernel.
+.Pp
+There are no command-line options.
+.\"
+.\" .Sh SEE ALSO
+.\" RFC2080 -- IPng for IPv6. G. Malkin, R. Minnear. January 1997.
diff --git a/usr.sbin/ifmcstat/ifmcstat.c b/usr.sbin/ifmcstat/ifmcstat.c
new file mode 100644
index 0000000..3167b96
--- /dev/null
+++ b/usr.sbin/ifmcstat/ifmcstat.c
@@ -0,0 +1,338 @@
+/*
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <kvm.h>
+#include <nlist.h>
+#include <string.h>
+#include <limits.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+# include <net/if_var.h>
+#endif
+#include <net/if_types.h>
+#include <net/if_dl.h>
+#include <netinet/in.h>
+#ifndef __NetBSD__
+# ifdef __FreeBSD__
+# define KERNEL
+# endif
+# include <netinet/if_ether.h>
+# ifdef __FreeBSD__
+# undef KERNEL
+# endif
+#else
+# include <net/if_ether.h>
+#endif
+#include <netinet/in_var.h>
+#include <arpa/inet.h>
+
+kvm_t *kvmd;
+
+struct nlist nl[] = {
+#define N_IFNET 0
+ { "_ifnet" },
+ { "" },
+};
+
+const char *inet6_n2a __P((struct in6_addr *));
+int main __P((void));
+char *ifname __P((struct ifnet *));
+void kread __P((u_long, void *, int));
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
+void acmc __P((struct ether_multi *));
+#endif
+void if6_addrlist __P((struct ifaddr *));
+void in6_multilist __P((struct in6_multi *));
+struct in6_multi * in6_multientry __P((struct in6_multi *));
+
+#if !defined(__NetBSD__) && !(defined(__FreeBSD__) && __FreeBSD__ >= 3) && !defined(__OpenBSD__)
+#ifdef __bsdi__
+struct ether_addr {
+ u_int8_t ether_addr_octet[6];
+};
+#endif
+static char *ether_ntoa __P((struct ether_addr *));
+#endif
+
+#define KREAD(addr, buf, type) \
+ kread((u_long)addr, (void *)buf, sizeof(type))
+
+const char *inet6_n2a(p)
+ struct in6_addr *p;
+{
+ static char buf[BUFSIZ];
+
+ if (IN6_IS_ADDR_UNSPECIFIED(p))
+ return "*";
+ return inet_ntop(AF_INET6, (void *)p, buf, sizeof(buf));
+}
+
+int main()
+{
+ char buf[_POSIX2_LINE_MAX], ifname[IFNAMSIZ];
+ struct ifnet *ifp, *nifp, ifnet;
+#ifndef __NetBSD__
+ struct arpcom arpcom;
+#else
+ struct ethercom ec;
+ struct sockaddr_dl sdl;
+#endif
+
+ if ((kvmd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, buf)) == NULL) {
+ perror("kvm_openfiles");
+ exit(1);
+ }
+ if (kvm_nlist(kvmd, nl) < 0) {
+ perror("kvm_nlist");
+ exit(1);
+ }
+ if (nl[N_IFNET].n_value == 0) {
+ printf("symbol %s not found\n", nl[N_IFNET].n_name);
+ exit(1);
+ }
+ KREAD(nl[N_IFNET].n_value, &ifp, struct ifnet *);
+ while (ifp) {
+ KREAD(ifp, &ifnet, struct ifnet);
+ printf("%s:\n", if_indextoname(ifnet.if_index, ifname));
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ if6_addrlist(ifnet.if_addrlist.tqh_first);
+ nifp = ifnet.if_list.tqe_next;
+#elif defined(__FreeBSD__) && __FreeBSD__ >= 3
+ if6_addrlist(TAILQ_FIRST(&ifnet.if_addrhead));
+ nifp = ifnet.if_link.tqe_next;
+#else
+ if6_addrlist(ifnet.if_addrlist);
+ nifp = ifnet.if_next;
+#endif
+
+#ifdef __NetBSD__
+ KREAD(ifnet.if_sadl, &sdl, struct sockaddr_dl);
+ if (sdl.sdl_type == IFT_ETHER) {
+ printf("\tenaddr %s",
+ ether_ntoa((struct ether_addr *)LLADDR(&sdl)));
+ KREAD(ifp, &ec, struct ethercom);
+ printf(" multicnt %d", ec.ec_multicnt);
+ acmc(ec.ec_multiaddrs.lh_first);
+ printf("\n");
+ }
+#elif defined(__FreeBSD__) && __FreeBSD__ >= 3
+ /* not supported */
+#else
+ if (ifnet.if_type == IFT_ETHER) {
+ KREAD(ifp, &arpcom, struct arpcom);
+ printf("\tenaddr %s",
+ ether_ntoa((struct ether_addr *)arpcom.ac_enaddr));
+ KREAD(ifp, &arpcom, struct arpcom);
+ printf(" multicnt %d", arpcom.ac_multicnt);
+#ifdef __OpenBSD__
+ acmc(arpcom.ac_multiaddrs.lh_first);
+#else
+ acmc(arpcom.ac_multiaddrs);
+#endif
+ printf("\n");
+ }
+#endif
+
+ ifp = nifp;
+ }
+
+ exit(0);
+ /*NOTREACHED*/
+}
+
+char *ifname(ifp)
+ struct ifnet *ifp;
+{
+ static char buf[BUFSIZ];
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ KREAD(ifp->if_xname, buf, IFNAMSIZ);
+#else
+ KREAD(ifp->if_name, buf, IFNAMSIZ);
+#endif
+ return buf;
+}
+
+void kread(addr, buf, len)
+ u_long addr;
+ void *buf;
+ int len;
+{
+ if (kvm_read(kvmd, addr, buf, len) != len) {
+ perror("kvm_read");
+ exit(1);
+ }
+}
+
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
+void acmc(am)
+ struct ether_multi *am;
+{
+ struct ether_multi em;
+
+ while (am) {
+ KREAD(am, &em, struct ether_multi);
+
+ printf("\n\t\t");
+ printf("%s -- ", ether_ntoa((struct ether_addr *)em.enm_addrlo));
+ printf("%s ", ether_ntoa((struct ether_addr *)&em.enm_addrhi));
+ printf("%d", em.enm_refcount);
+#if !defined(__NetBSD__) && !defined(__OpenBSD__)
+ am = em.enm_next;
+#else
+ am = em.enm_list.le_next;
+#endif
+ }
+}
+#endif
+
+void
+if6_addrlist(ifap)
+ struct ifaddr *ifap;
+{
+ static char in6buf[BUFSIZ];
+ struct ifaddr ifa;
+ struct sockaddr sa;
+ struct in6_ifaddr if6a;
+ struct in6_multi *mc = 0;
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ struct ifaddr *ifap0;
+#endif /* __FreeBSD__ >= 3 */
+
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ ifap0 = ifap;
+#endif /* __FreeBSD__ >= 3 */
+ while (ifap) {
+ KREAD(ifap, &ifa, struct ifaddr);
+ if (ifa.ifa_addr == NULL)
+ goto nextifap;
+ KREAD(ifa.ifa_addr, &sa, struct sockaddr);
+ if (sa.sa_family != PF_INET6)
+ goto nextifap;
+ KREAD(ifap, &if6a, struct in6_ifaddr);
+ printf("\tinet6 %s\n",
+ inet_ntop(AF_INET6,
+ (const void *)&if6a.ia_addr.sin6_addr,
+ in6buf, sizeof(in6buf)));
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
+ mc = mc ? mc : if6a.ia6_multiaddrs.lh_first;
+#endif
+ nextifap:
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ ifap = ifa.ifa_list.tqe_next;
+#elif defined(__FreeBSD__) && __FreeBSD__ >= 3
+ ifap = ifa.ifa_link.tqe_next;
+#else
+ ifap = ifa.ifa_next;
+#endif /* __FreeBSD__ >= 3 */
+ }
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ if (ifap0) {
+ struct ifnet ifnet;
+ struct ifmultiaddr ifm, *ifmp = 0;
+ struct sockaddr_in6 sin6;
+ struct in6_multi in6m;
+ struct sockaddr_dl sdl;
+ int in6_multilist_done = 0;
+
+ KREAD(ifap0, &ifa, struct ifaddr);
+ KREAD(ifa.ifa_ifp, &ifnet, struct ifnet);
+ if (ifnet.if_multiaddrs.lh_first)
+ ifmp = ifnet.if_multiaddrs.lh_first;
+ while (ifmp) {
+ KREAD(ifmp, &ifm, struct ifmultiaddr);
+ if (ifm.ifma_addr == NULL)
+ goto nextmulti;
+ KREAD(ifm.ifma_addr, &sa, struct sockaddr);
+ if (sa.sa_family != AF_INET6)
+ goto nextmulti;
+ (void)in6_multientry((struct in6_multi *)
+ ifm.ifma_protospec);
+ if (ifm.ifma_lladdr == 0)
+ goto nextmulti;
+ KREAD(ifm.ifma_lladdr, &sdl, struct sockaddr_dl);
+ printf("\t\t\tmcast-macaddr %s multicnt %d\n",
+ ether_ntoa((struct ether_addr *)LLADDR(&sdl)),
+ ifm.ifma_refcount);
+ nextmulti:
+ ifmp = ifm.ifma_link.le_next;
+ }
+ }
+#else
+ if (mc)
+ in6_multilist(mc);
+#endif
+}
+
+struct in6_multi *
+in6_multientry(mc)
+ struct in6_multi *mc;
+{
+ static char mcbuf[BUFSIZ];
+ struct in6_multi multi;
+
+ KREAD(mc, &multi, struct in6_multi);
+ printf("\t\tgroup %s\n", inet_ntop(AF_INET6,
+ (const void *)&multi.in6m_addr,
+ mcbuf, sizeof(mcbuf)));
+ return(multi.in6m_entry.le_next);
+}
+
+void
+in6_multilist(mc)
+ struct in6_multi *mc;
+{
+ while (mc)
+ mc = in6_multientry(mc);
+}
+
+#if !defined(__NetBSD__) && !(defined(__FreeBSD__) && __FreeBSD__ >= 3) && !defined(__OpenBSD__)
+static char *
+ether_ntoa(e)
+ struct ether_addr *e;
+{
+ static char buf[20];
+ u_char *p;
+
+ p = (u_char *)e;
+
+ snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x",
+ p[0], p[1], p[2], p[3], p[4], p[5]);
+ return buf;
+}
+#endif
diff --git a/usr.sbin/prefix/Makefile b/usr.sbin/prefix/Makefile
new file mode 100644
index 0000000..323d6d3
--- /dev/null
+++ b/usr.sbin/prefix/Makefile
@@ -0,0 +1,12 @@
+# From: @(#)Makefile 8.1 (Berkeley) 6/5/93
+# $Id: Makefile,v 1.1.1.1 1999/08/08 23:31:13 itojun Exp $
+# $FreeBSD$
+
+PROG= prefix
+SRCS= prefix.c
+
+MAN8= prefix.8
+
+CFLAGS+=-DINET6
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/prefix/prefix.8 b/usr.sbin/prefix/prefix.8
new file mode 100644
index 0000000..e3a35d0
--- /dev/null
+++ b/usr.sbin/prefix/prefix.8
@@ -0,0 +1,217 @@
+.\" 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.
+.\"
+.\" $Id: prefix.8,v 1.1.1.1 1999/08/08 23:31:13 itojun Exp $
+.\" $FreeBSD$
+.\"
+.Dd October 10, 1998
+.Dt PREFIX 8
+.Os KAME
+.Sh NAME
+.Nm prefix
+.Nd configure network interface prefixes
+.Sh SYNOPSIS
+.Nm prefix
+.Ar interface prefix
+.Op Ar parameters
+.Op Cm set|delete
+.Nm prefix
+.Ar interface
+.Cm matchpr
+.Ar match_prefix
+.Cm mp_len
+.Ar match_prefix_len
+.Cm usepr
+.Ar use_prefix
+.Cm up_uselen
+.Ar use_prefix_len
+.Op Ar parameters
+.Op Cm add|change|setglobal
+.Nm prefix
+.Fl a
+.Op Fl d
+.Op Fl u
+.Cm matchpr
+.Ar match_prefix
+.Cm mp_len
+.Ar match_prefix_len
+.Cm usepr
+.Ar use_prefix
+.Cm up_uselen
+.Ar use_prefix_uselen
+.Op Ar parameters
+.Op Cm add|change|setglobal
+.Sh DESCRIPTION
+.Nm Prefix
+is used to assign an prefix
+to a network interface and/or renumbering
+network interface prefixes.
+.Nm Prefix
+must be used at boot time to define the network prefix
+of each interface present on a machine; it may also be used at
+a later time to renumbering multiple interface's prefixes
+and other prefix related parameters.
+.Nm Prefix
+is router-only command, so you must do following to use it.
+.Dl % sysctl -w net.inet6.ip6.forwarding=1
+If net.inet6.ip6.forwarding is set to 0,
+.Nm Prefix
+command fails by EPERM error.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Ar interface
+Specify an
+.Ar interface
+for which
+.Ar prefix
+is/are assigned or renumbered to.
+This parameter is a string of the form
+.Dq name unit ,
+for example,
+.Dq en0 .
+.It Ar prefix
+Assign/Delete an
+.Ar prefix
+to a network interface.
+.It Cm prefixlen Ar len
+Specify that
+.Ar len
+bits are reserved as identifier for IPv6 sub-networks in
+.Ar prefix.
+The
+.Ar len
+must be integer, and for syntactical reason it must be between 0 to 128.
+It is almost always 64 under the current IPv6 assignment rule.
+If the parameter is ommitted, 64 is used.
+.It Cm matchpr Ar match_prefix
+Specify
+.Ar match_prefix
+that is used for matching with preassigned prefixes to which
+.Cm add|change|setglobal
+command should be applied.
+.It Cm mp_len Ar match_prefix_len
+Specify the starting part of
+.Ar match_prefix
+to be used for matching with preassigned prefixes, as decimal bit number.
+.It Cm mp_minlen Ar match_prefix_minlen
+Specify the minimum length of prefixes which is allowed to be matched to
+.Ar match_prefix ,
+as decimal bit number.
+.Cm add|change|setglobal
+is not applied to preassigned prefixes with smaller prefix len than
+.Ar match_prefix_minlen .
+.It Cm mp_maxlen Ar match_prefix_maxlen
+Specify the maximum length of prefixes which is allowed to be matched to
+.Ar match_prefix ,
+as decimal bit number.
+.Cm add|change|setglobal
+is not applied to preassigned prefixes with bigger prefix len than
+.Ar match_prefix_maxlen .
+.It Cm usepr Ar use_prefix
+Specify
+.Ar use_prefix
+that is used for prefixes to be added on
+.Cm add|change|setglobal
+command.
+.It Cm up_uselen Ar use_prefix_uselen
+Specify the starting part of
+.Ar use_prefix
+copied to the starting part of prefixes to be added on
+.Cm add|change|setglobal
+command, as decimal bit number.
+.It Cm up_keeplen Ar use_prefix_keeplen
+Specify the midium part of
+.Ar use_prefix
+just next to the starting part specified by
+.Ar use_prefix_uselen
+, as decimal bit number.
+Contiguous bits part in the same bit position of an existent prefix
+matched with
+.Ar match_prefix
+is copied to the same bit position of prefixes to be added.
+.It Cm pltime Ar time
+Assign an
+.Ar time
+as prefix preferred life time for a prefix to be added.
+Valid value for
+.Ar time
+is decimal seconds number or special format as "d00h00m00s00",
+where 00 can take any decimal number, and "d" means days, "h" means hours,
+"m" means minutes, "s" means seconds. And alternatively, special keyword
+"infinity" can be also be specified.
+.It Cm vltime Ar time
+Assign an
+.Ar time
+as prefix valid life time for a prefix to be added.
+Valid value for
+.Ar time
+is same as for
+.Cm pltime.
+.It Cm raf_auto
+Enable the autonomous address auto configuration for the prefix to be
+added.
+.It Fl raf_auto
+Disable the autonomous address auto configuration for the prefix to be
+added.
+.It Cm raf_onlink
+Let the prefix to be added to have onlink nature for the assigned
+interface.
+.It Fl raf_onlink
+Let the prefix to be added not to have onlink nature for the assigned
+interface.
+.It Cm rrf_decrprefd
+Enable the decrementation of the pltime.
+.It Fl rrf_decrprefd
+Disable the decrementation of the pltime.
+.It Cm rrf_decrvalid
+Enable the decrementation of the vltime.
+.It Fl rrf_decrvalid
+Disable the decrementation of the vltime.
+.El
+.Sh DIAGNOSTICS
+Messages indicating the specified interface does not exist, the
+requested prefix is unknown, or the user is not privileged and
+tried to alter an interface's configuration.
+.Sh SEE ALSO
+M. Crawford,
+Router Renumbering for IPv6,
+internet-draft,
+draft-ietf-ipngwg-router-renum-05.txt
+.Pp
+.Xr ifconfig 8 ,
+.Xr netstat 1 ,
+.Xr netintro 4 ,
+.Xr rc 8 ,
+.Xr routed 8
+.Sh HISTORY
+The
+.Nm
+command first appeared in WIDE/KAME IPv6 protocol stack kit.
+.\"
+.\" .Sh BUGS
+.\" (to be written)
diff --git a/usr.sbin/prefix/prefix.c b/usr.sbin/prefix/prefix.c
new file mode 100644
index 0000000..53c9546
--- /dev/null
+++ b/usr.sbin/prefix/prefix.c
@@ -0,0 +1,592 @@
+/*
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+
+#include <net/if.h>
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+#include <net/if_var.h>
+#endif /* __FreeBSD__ >= 3 */
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define DEF_ADVVALIDLIFETIME 2592000
+#define DEF_ADVPREFERREDLIFETIME 604800
+struct in6_prefixreq prereq = {{NULL}, /* interface name */
+ PR_ORIG_STATIC,
+ 64, /* default plen */
+ 2592000, /* vltime=30days */
+ 604800, /* pltime=7days */
+ /* ra onlink=1 autonomous=1 */
+ {{1,1,0}},
+ {NULL}}; /* prefix */
+struct in6_rrenumreq rrreq = {{NULL}, /* interface name */
+ PR_ORIG_STATIC, /* default origin */
+ 64, /* default match len */
+ 0, /* default min match len */
+ 128, /* default max match len */
+ 0, /* default uselen */
+ 0, /* default keeplen */
+ {1,1,0}, /* default raflag mask */
+ 2592000, /* vltime=30days */
+ 604800, /* pltime=7days */
+ /* ra onlink=1 autonomous=1 */
+ {{1,1,0}},
+ {NULL}, /* match prefix */
+ {NULL} /* use prefix */
+ };
+
+#define C(x) ((caddr_t) &x)
+
+struct prefix_cmds {
+ const char *errmsg;
+ int cmd;
+ caddr_t req;
+} prcmds[] = {
+ {"SIOCSIFPREFIX_IN6 failed", SIOCSIFPREFIX_IN6, C(prereq)},
+ {"SIOCDIFPREFIX_IN6 failed", SIOCDIFPREFIX_IN6, C(prereq)},
+ {"SIOCAIFPREFIX_IN6 failed", SIOCAIFPREFIX_IN6, C(rrreq)},
+ {"SIOCCIFPREFIX_IN6 failed", SIOCCIFPREFIX_IN6, C(rrreq)},
+ {"SIOCSGIFPREFIX_IN6 failed", SIOCSGIFPREFIX_IN6, C(rrreq)}
+};
+
+#define PREF_CMD_SET 0
+#define PREF_CMD_DELETE 1
+#define PREF_CMD_ADD 2
+#define PREF_CMD_CHANGE 3
+#define PREF_CMD_SETGLOBAL 4
+#define PREF_CMD_MAX 5
+
+u_int prcmd = PREF_CMD_SET; /* default command */
+
+char name[32];
+int flags;
+
+int newprefix_setdel, newprefix_match, newprefix_use, newprefix_uselen,
+ newprefix_keeplen;
+
+char ntop_buf[INET6_ADDRSTRLEN]; /*inet_ntop()*/
+
+void Perror __P((const char *cmd));
+int prefix __P((int argc, char *const *argv));
+void usage __P((void));
+void setlifetime __P((const char *atime, u_int32_t *btime));
+int all, explicit_prefix = 0;
+
+typedef void c_func __P((const char *cmd, int arg));
+c_func set_vltime, set_pltime, set_raf_onlink,
+ set_raf_auto, set_rrf_decrvalid, set_rrf_decrprefd,
+ get_setdelprefix, get_matchprefix, get_useprefix, set_matchlen,
+ set_match_minlen, set_match_maxlen,
+ set_use_uselen, set_use_keeplen, set_prefix_cmd;
+
+void getprefixlen __P((const char *, int));
+void getprefix __P((const char *, int));
+
+#define NEXTARG 0xffffff
+
+const
+struct cmd {
+ const char *c_name;
+ int c_parameter; /* NEXTARG means next argv */
+ void (*c_func) __P((const char *, int));
+} cmds[] = {
+ { "set", PREF_CMD_SET, set_prefix_cmd },
+ { "delete", PREF_CMD_DELETE, set_prefix_cmd },
+ { "prefixlen", NEXTARG, getprefixlen },
+ { "add", PREF_CMD_ADD, set_prefix_cmd },
+ { "change", PREF_CMD_CHANGE, set_prefix_cmd },
+ { "setglobal", PREF_CMD_SETGLOBAL, set_prefix_cmd },
+ { "matchpr", NEXTARG, get_matchprefix },
+ { "usepr", NEXTARG, get_useprefix },
+ { "mp_len", NEXTARG, set_matchlen },
+ { "mp_minlen", NEXTARG, set_match_minlen },
+ { "mp_maxlen", NEXTARG, set_match_maxlen },
+ { "up_uselen", NEXTARG, set_use_uselen },
+ { "up_keeplen", NEXTARG, set_use_keeplen },
+ { "vltime", NEXTARG, set_vltime },
+ { "pltime", NEXTARG, set_pltime },
+ { "raf_onlink", 1, set_raf_onlink },
+ { "-raf_onlink", 0, set_raf_onlink },
+ { "raf_auto", 1, set_raf_auto },
+ { "-raf_auto", 0, set_raf_auto },
+ { "rrf_decrvalid", 1, set_rrf_decrvalid },
+ { "-rrf_decrvalid", 0, set_rrf_decrvalid },
+ { "rrf_decrprefd", 1, set_rrf_decrprefd },
+ { "-rrf_decrprefd", 0, set_rrf_decrprefd },
+ { 0, 0, get_setdelprefix },
+ { 0, 0, 0 },
+};
+
+
+void
+usage()
+{
+ fprintf(stderr, "%s",
+ "usage: prefix interface prefix_value [parameters] [set|delete]\n"
+ " prefix interface\n"
+ " matchpr matchpr_value mp_len mp_len_value\n"
+ " usepr usepr_value up_uselen up_uselen_value\n"
+ " [parameters] [add|change|setglobal]\n"
+ " prefix -a [-d] [-u]\n"
+ " matchpr matchpr_value mp_len mp_len_value\n"
+ " usepr usepr_value up_uselen up_uselen_value\n"
+ " [parameters] [add|change|setglobal]\n");
+ exit(1);
+}
+
+int
+main(argc, argv)
+ int argc;
+ char *const *argv;
+{
+ int c;
+ int downonly, uponly;
+ int foundit = 0;
+ int addrcount;
+ struct if_msghdr *ifm, *nextifm;
+ struct ifa_msghdr *ifam;
+ struct sockaddr_dl *sdl;
+ char *buf, *lim, *next;
+
+
+ size_t needed;
+ int mib[6];
+
+ /* Parse leading line options */
+ all = downonly = uponly = 0;
+ while ((c = getopt(argc, argv, "adu")) != -1) {
+ switch (c) {
+ case 'a': /* scan all interfaces */
+ all++;
+ break;
+ case 'd': /* restrict scan to "down" interfaces */
+ downonly++;
+ break;
+ case 'u': /* restrict scan to "up" interfaces */
+ uponly++;
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ /* nonsense.. */
+ if (uponly && downonly)
+ usage();
+
+ if (!all) {
+ /* not listing, need an argument */
+ if (argc < 1)
+ usage();
+
+ strncpy(name, *argv, sizeof(name));
+ argc--, argv++;
+ }
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = AF_INET6; /* address family */
+ mib[4] = NET_RT_IFLIST;
+ mib[5] = 0;
+
+ if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
+ errx(1, "iflist-sysctl-estimate");
+ if ((buf = malloc(needed)) == NULL)
+ errx(1, "malloc");
+ if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
+ errx(1, "actual retrieval of interface table");
+ lim = buf + needed;
+
+ next = buf;
+ while (next < lim) {
+
+ ifm = (struct if_msghdr *)next;
+
+ if (ifm->ifm_type == RTM_IFINFO) {
+ sdl = (struct sockaddr_dl *)(ifm + 1);
+ flags = ifm->ifm_flags;
+ } else {
+ fprintf(stderr, "out of sync parsing NET_RT_IFLIST\n");
+ fprintf(stderr, "expected %d, got %d\n", RTM_IFINFO,
+ ifm->ifm_type);
+ fprintf(stderr, "msglen = %d\n", ifm->ifm_msglen);
+ fprintf(stderr, "buf:%p, next:%p, lim:%p\n", buf, next,
+ lim);
+ exit (1);
+ }
+
+ next += ifm->ifm_msglen;
+ ifam = NULL;
+ addrcount = 0;
+ while (next < lim) {
+
+ nextifm = (struct if_msghdr *)next;
+
+ if (nextifm->ifm_type != RTM_NEWADDR)
+ break;
+
+ if (ifam == NULL)
+ ifam = (struct ifa_msghdr *)nextifm;
+
+ addrcount++;
+ next += nextifm->ifm_msglen;
+ }
+
+ if (all) {
+ if (uponly)
+ if ((flags & IFF_UP) == 0)
+ continue; /* not up */
+ if (downonly)
+ if (flags & IFF_UP)
+ continue; /* not down */
+ strncpy(name, sdl->sdl_data, sdl->sdl_nlen);
+ name[sdl->sdl_nlen] = '\0';
+ } else {
+ if (strlen(name) != sdl->sdl_nlen)
+ continue; /* not same len */
+ if (strncmp(name, sdl->sdl_data, sdl->sdl_nlen) != 0)
+ continue; /* not same name */
+ }
+
+ if (argc > 0)
+ prefix(argc, argv);
+#if 0
+ else {
+ /* TODO: print prefix status by sysctl */
+ }
+#endif
+
+ if (all == 0) {
+ foundit++; /* flag it as 'done' */
+ break;
+ }
+ }
+ free(buf);
+
+ if (all == 0 && foundit == 0)
+ errx(1, "interface %s does not exist", name);
+
+
+ exit (0);
+}
+
+
+int
+prefix(int argc, char *const *argv)
+{
+ int s;
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
+ err(1, "socket");
+
+ while (argc > 0) {
+ register const struct cmd *p;
+
+ for (p = cmds; p->c_name; p++)
+ if (strcmp(*argv, p->c_name) == 0)
+ break;
+ if (p->c_func) {
+ if (p->c_parameter == NEXTARG) {
+ if (argv[1] == NULL)
+ errx(1, "'%s' requires argument",
+ p->c_name);
+ (*p->c_func)(argv[1], 0);
+ argc--, argv++;
+ } else
+ (*p->c_func)(*argv, p->c_parameter);
+ }
+ argc--, argv++;
+ }
+ if (prcmd > PREF_CMD_MAX) {
+ Perror("ioctl: unknown prefix cmd");
+ goto end;
+ }
+ if (prcmd == PREF_CMD_SET || prcmd == PREF_CMD_DELETE) {
+ if (!newprefix_setdel)
+ usage();
+ } else { /* ADD|CHANGE|SETGLOBAL */
+ if (!newprefix_match)
+ usage();
+ }
+ if (!newprefix_use)
+ rrreq.irr_u_uselen = 0; /* make clear that no use_prefix */
+ else if (newprefix_keeplen == NULL && rrreq.irr_u_uselen < 64)
+ /* init keeplen to make uselen + keeplen equal 64 */
+ rrreq.irr_u_keeplen = 64 - rrreq.irr_u_uselen;
+ if (explicit_prefix == 0) {
+ /* Aggregatable address architecture defines all prefixes
+ are 64. So, it is convenient to set prefixlen to 64 if
+ it is not specified. */
+ getprefixlen("64", 0);
+ }
+ strncpy(prcmds[prcmd].req, name, IFNAMSIZ);
+ if (ioctl(s, prcmds[prcmd].cmd, prcmds[prcmd].req) < 0) {
+ if (all && errno == EADDRNOTAVAIL)
+ goto end;
+ Perror(prcmds[prcmd].errmsg);
+ }
+ end:
+ close(s);
+ return(0);
+}
+#define PREFIX 0
+#define MPREFIX 1
+#define UPREFIX 2
+
+void
+Perror(cmd)
+ const char *cmd;
+{
+ switch (errno) {
+
+ case ENXIO:
+ errx(1, "%s: no such interface", cmd);
+ break;
+
+ case EPERM:
+ errx(1, "%s: permission denied", cmd);
+ break;
+
+ default:
+ err(1, "%s", cmd);
+ }
+}
+
+#define SIN6(x) ((struct sockaddr_in6 *) &(x))
+struct sockaddr_in6 *sin6tab[] = {
+SIN6(prereq.ipr_prefix), SIN6(rrreq.irr_matchprefix),
+SIN6(rrreq.irr_useprefix)};
+
+void
+getprefixlen(const char *plen, int unused)
+{
+ int len = atoi(plen);
+
+ if ((len < 0) || (len > 128))
+ errx(1, "%s: bad value", plen);
+
+ /* set plen for prereq */
+ prereq.ipr_plen = len;
+ explicit_prefix = 1;
+}
+
+void
+getprefix(const char *prefix, int which)
+{
+ register struct sockaddr_in6 *sin = sin6tab[which];
+
+ /*
+ * Delay the ioctl to set the interface prefix until flags are all set.
+ * The prefix interpretation may depend on the flags,
+ * and the flags may change when the prefix is set.
+ */
+
+ sin->sin6_len = sizeof(*sin);
+ sin->sin6_family = AF_INET6;
+
+ if (inet_pton(AF_INET6, prefix, &sin->sin6_addr) != 1)
+ errx(1, "%s: bad value", prefix);
+}
+
+void
+get_setdelprefix(const char *prefix, int unused)
+{
+ newprefix_setdel++;
+ getprefix(prefix, PREFIX);
+}
+
+void
+get_matchprefix(const char *prefix, int unused)
+{
+ newprefix_match++;
+ prcmd = (prcmd == PREF_CMD_SET) ? PREF_CMD_ADD : prcmd;
+ getprefix(prefix, MPREFIX);
+}
+
+void
+get_useprefix(const char *prefix, int unused)
+{
+ newprefix_use++;
+ if (newprefix_uselen == 0)
+ rrreq.irr_u_uselen = 64;
+ getprefix(prefix, UPREFIX);
+}
+
+static int
+get_plen(const char *plen)
+{
+ int len;
+
+ len = atoi(plen);
+ if ((len < 0) || (len > 128))
+ errx(1, "%s: bad value", plen);
+ return len;
+}
+
+void
+set_matchlen(const char *plen, int unused)
+{
+ rrreq.irr_m_len = get_plen(plen);
+}
+
+void
+set_match_minlen(const char *plen, int unused)
+{
+ rrreq.irr_m_minlen = get_plen(plen);
+}
+
+void
+set_match_maxlen(const char *plen, int unused)
+{
+ rrreq.irr_m_maxlen = get_plen(plen);
+}
+
+void
+set_use_uselen(const char *plen, int unused)
+{
+ newprefix_uselen++;
+ rrreq.irr_u_uselen = get_plen(plen);
+}
+
+void
+set_use_keeplen(const char *plen, int unused)
+{
+ newprefix_keeplen++;
+ rrreq.irr_u_keeplen = get_plen(plen);
+}
+
+void
+set_vltime(const char *ltime, int unused)
+{
+ setlifetime(ltime, &prereq.ipr_vltime);
+ rrreq.irr_vltime = prereq.ipr_vltime;
+}
+
+void
+set_pltime(const char *ltime, int unused)
+{
+ setlifetime(ltime, &prereq.ipr_pltime);
+ rrreq.irr_pltime = prereq.ipr_pltime;
+}
+
+void
+set_raf_onlink(const char *unused, int value)
+{
+ /* raflagmask is only meaningful when newprefix_rrenum */
+ rrreq.irr_raflagmask.onlink = 1;
+ prereq.ipr_flags.prf_ra.onlink =
+ rrreq.irr_flags.prf_ra.onlink = value ? 1 : 0;
+}
+
+void
+set_raf_auto(const char *unused, int value)
+{
+ /* only meaningful when newprefix_rrenum */
+ rrreq.irr_raflagmask.autonomous = 1;
+ prereq.ipr_flags.prf_ra.autonomous =
+ rrreq.irr_flags.prf_ra.autonomous = value ? 1 : 0;
+}
+
+void
+set_rrf_decrvalid(const char *unused, int value)
+{
+ prereq.ipr_flags.prf_rr.decrvalid =
+ rrreq.irr_flags.prf_rr.decrvalid = value ? 1 : 0;
+}
+
+void
+set_rrf_decrprefd(const char *unused, int value)
+{
+ prereq.ipr_flags.prf_rr.decrprefd =
+ rrreq.irr_flags.prf_rr.decrprefd = value ? 1 : 0;
+}
+
+void
+set_prefix_cmd(const char *unused, int cmd)
+{
+ prcmd = cmd;
+}
+
+void
+setlifetime(const char *atime, u_int32_t *btime)
+{
+ int days = 0, hours = 0, minutes = 0, seconds = 0;
+ u_int32_t ttime;
+ char *check;
+
+ if (strcmp(atime, "infinity") == 0) {
+ *btime = 0xffffffff;
+ return;
+ }
+ ttime = strtoul(atime, &check ,10) & 0xffffffff;
+ if (*check == '\0') {
+ *btime = ttime;
+ return;
+ }
+ if (sscanf(atime, "d%2dh%2dm%2ds%2d", &days, &hours, &minutes,
+ &seconds) < 0) {
+ Perror("wrong time format: valid is d00h00m00s00, \n"
+ "where 00 can be any octal number, \n"
+ "\'d\' is for days, \'h\' is for hours, \n"
+ "\'m\' is for minutes, and \'s\' is for seconds \n");
+ return;
+ }
+ *btime = 0;
+ *btime += seconds;
+ *btime += minutes * 60;
+ *btime += hours * 3600;
+ *btime += days * 86400;
+ return;
+}
diff --git a/usr.sbin/rip6query/Makefile b/usr.sbin/rip6query/Makefile
new file mode 100644
index 0000000..8a0cd40
--- /dev/null
+++ b/usr.sbin/rip6query/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+# $FreeBSD$
+
+PROG= rip6query
+MAN8= rip6query.8
+
+CFLAGS+=-DINET6 -I${.CURDIR}/../route6d
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rip6query/rip6query.8 b/usr.sbin/rip6query/rip6query.8
new file mode 100644
index 0000000..bd64068
--- /dev/null
+++ b/usr.sbin/rip6query/rip6query.8
@@ -0,0 +1,62 @@
+.\"
+.\" Copyright (C) 1998 and 1999 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.
+.\"
+.\" $Id: rip6query.8,v 1.1 1999/10/07 05:36:36 itojun Exp $
+.\" $FreeBSD$
+.\"
+.Dd Oct 7, 1999
+.Dt RIP6QUERY 8
+.Os KAME
+.Sh NAME
+.Nm rip6query
+.Nd RIPng debugging tool
+.\"
+.Sh SYNOPSIS
+.Nm
+.Op Fl I Ar interface
+.Ar destination
+.\"
+.Sh DESCRIPTION
+.Nm
+requests remote RIPng daemon on
+.Ar destionation
+to dump RIPng routing information.
+.Fl I
+lets you specify outgoing
+.Ar interface
+for the query packet,
+and is useful when link-local address is specified for
+.Ar destination .
+.\"
+.Sh SEE ALSO
+.Xr route6d 8 .
+.\"
+.Sh HISTORY
+The
+.Nm
+command first appeared in WIDE Hydrangea IPv6 protocol stack kit.
diff --git a/usr.sbin/rip6query/rip6query.c b/usr.sbin/rip6query/rip6query.c
new file mode 100644
index 0000000..59e7932
--- /dev/null
+++ b/usr.sbin/rip6query/rip6query.c
@@ -0,0 +1,206 @@
+/*
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <stdio.h>
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <signal.h>
+#include <err.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+#include <net/if_var.h>
+#endif /* __FreeBSD__ >= 3 */
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include "route6d.h"
+
+/* wrapper for KAME-special getnameinfo() */
+#ifndef NI_WITHSCOPEID
+#define NI_WITHSCOPEID 0
+#endif
+
+int s;
+extern int errno;
+struct sockaddr_in6 sin6;
+struct rip6 *ripbuf;
+
+#define RIPSIZE(n) (sizeof(struct rip6) + (n-1) * sizeof(struct netinfo6))
+
+int main __P((int, char **));
+static void usage __P((void));
+static const char *sa_n2a __P((struct sockaddr *));
+static const char *inet6_n2a __P((struct in6_addr *));
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ struct netinfo6 *np;
+ struct sockaddr_in6 fsock;
+ int i, n, len, flen;
+ int c;
+ extern char *optarg;
+ extern int optind;
+ int ifidx = -1;
+ int error;
+ char pbuf[10];
+ struct addrinfo hints, *res;
+
+ while ((c = getopt(argc, argv, "I:")) != EOF) {
+ switch (c) {
+ case 'I':
+ ifidx = if_nametoindex(optarg);
+ if (ifidx == 0) {
+ errx(1, "invalid interface %s", optarg);
+ /*NOTREACHED*/
+ }
+ break;
+ default:
+ usage();
+ exit(1);
+ /*NOTREACHED*/
+ }
+ }
+ argv += optind;
+ argc -= optind;
+
+ if (argc != 1) {
+ usage();
+ exit(-1);
+ }
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ err(1, "socket");
+ /*NOTREACHED*/
+ }
+
+ /* getaddrinfo is preferred for addr@ifname syntax */
+ snprintf(pbuf, sizeof(pbuf), "%d", RIP6_PORT);
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET6;
+ hints.ai_socktype = SOCK_STREAM;
+ error = getaddrinfo(argv[0], pbuf, &hints, &res);
+ if (error) {
+ errx(1, "%s: %s", argv[0], gai_strerror(error));
+ /*NOTREACHED*/
+ }
+ if (res->ai_next) {
+ errx(1, "%s: %s", argv[0], "resolved to multiple addrs");
+ /*NOTREACHED*/
+ }
+ if (sizeof(sin6) != res->ai_addrlen) {
+ errx(1, "%s: %s", argv[0], "invalid addrlen");
+ /*NOTREACHED*/
+ }
+ memcpy(&sin6, res->ai_addr, res->ai_addrlen);
+ if (ifidx >= 0)
+ sin6.sin6_scope_id = ifidx;
+
+ if ((ripbuf = (struct rip6 *)malloc(BUFSIZ)) == NULL) {
+ err(1, "malloc");
+ /*NOTREACHED*/
+ }
+ ripbuf->rip6_cmd = RIP6_REQUEST;
+ ripbuf->rip6_vers = RIP6_VERSION;
+ ripbuf->rip6_res1[0] = 0;
+ ripbuf->rip6_res1[1] = 0;
+ np = ripbuf->rip6_nets;
+ bzero(&np->rip6_dest, sizeof(struct in6_addr));
+ np->rip6_tag = 0;
+ np->rip6_plen = 0;
+ np->rip6_metric = HOPCNT_INFINITY6;
+ if (sendto(s, ripbuf, RIPSIZE(1), 0, (struct sockaddr *)&sin6,
+ sizeof(struct sockaddr_in6)) < 0) {
+ err(1, "send");
+ /*NOTREACHED*/
+ }
+ do {
+ flen = sizeof(fsock);
+ if ((len = recvfrom(s, ripbuf, BUFSIZ, 0,
+ (struct sockaddr *)&fsock, &flen)) < 0) {
+ err(1, "recvfrom");
+ /*NOTREACHED*/
+ }
+ printf("Response from %s len %d\n",
+ sa_n2a((struct sockaddr *)&fsock), len);
+ n = (len - sizeof(struct rip6) + sizeof(struct netinfo6)) /
+ sizeof(struct netinfo6);
+ np = ripbuf->rip6_nets;
+ for (i = 0; i < n; i++, np++) {
+ printf("\t%s/%d [%d]", inet6_n2a(&np->rip6_dest),
+ np->rip6_plen, np->rip6_metric);
+ if (np->rip6_tag)
+ printf(" tag=0x%x", ntohs(np->rip6_tag));
+ printf("\n");
+ }
+ } while (len == RIPSIZE(24));
+
+ exit(0);
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "Usage: rip6query [-I iface] address\n");
+}
+
+/* getnameinfo() is preferred as we may be able to show ifindex as ifname */
+static const char *
+sa_n2a(sa)
+ struct sockaddr *sa;
+{
+ static char buf[BUFSIZ];
+
+ if (getnameinfo(sa, sa->sa_len, buf, sizeof(buf),
+ NULL, 0, NI_NUMERICHOST | NI_WITHSCOPEID) != 0) {
+ snprintf(buf, sizeof(buf), "%s", "(invalid)");
+ }
+ return buf;
+}
+
+static const char *
+inet6_n2a(addr)
+ struct in6_addr *addr;
+{
+ static char buf[BUFSIZ];
+
+ return inet_ntop(AF_INET6, addr, buf, sizeof(buf));
+}
diff --git a/usr.sbin/route6d/Makefile b/usr.sbin/route6d/Makefile
new file mode 100644
index 0000000..ed5354e
--- /dev/null
+++ b/usr.sbin/route6d/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+# $FreeBSD$
+
+PROG= route6d
+MAN8= route6d.8
+
+CFLAGS+= -Dss_len=__ss_len -Dss_family=__ss_family -DADVAPI -DINET6
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/route6d/misc/chkrt b/usr.sbin/route6d/misc/chkrt
new file mode 100755
index 0000000..f57a376
--- /dev/null
+++ b/usr.sbin/route6d/misc/chkrt
@@ -0,0 +1,64 @@
+#!/usr/bin/perl
+#
+# $FreeBSD$
+#
+$dump="/var/tmp/route6d_dump";
+$pidfile="/var/run/route6d.pid";
+
+system("rm -f $dump");
+
+open(FD, "< $pidfile") || die "Can not open $pidfile";
+$_ = <FD>;
+chop;
+close(FD);
+system("kill -INT $_");
+
+open(NS, "/usr/local/v6/bin/netstat -r -n|") || die "Can not open netstat";
+while (<NS>) {
+ chop;
+ next unless (/^3f/ || /^5f/);
+ @f = split(/\s+/);
+ $gw{$f[0]} = $f[1];
+ $int{$f[0]} = $f[3];
+}
+close(NS);
+
+$err=0;
+sleep(2);
+open(FD, "< $dump") || die "Can not open $dump";
+while (<FD>) {
+ chop;
+ next unless (/^ 3f/ || /^ 5f/);
+ @f = split(/\s+/);
+ $dst = $f[1];
+ $f[2] =~ /if\(\d:([a-z0-9]+)\)/;
+ $intf = $1;
+ $f[3] =~ /gw\(([a-z0-9:]+)\)/;
+ $gateway = $1;
+ $f[4] =~ /\[(\d+)\]/;
+ $metric = $1;
+ $f[5] =~ /age\((\d+)\)/;
+ $age = $1;
+ unless (defined($gw{$dst})) {
+ print "NOT FOUND: $dst $intf $gateway $metric $age\n";
+ $err++;
+ next;
+ }
+ if ($gw{$dst} ne $gateway && $gw{$dst} !~ /link#\d+/) {
+ print "WRONG GW: $dst $intf $gateway $metric $age\n";
+ print "kernel gw: $gw{$dst}\n";
+ $err++;
+ next;
+ }
+ if ($int{$dst} ne $intf) {
+ print "WRONG IF: $dst $intf $gateway $metric $age\n";
+ print "kernel if: $int{$dst}\n";
+ $err++;
+ next;
+ }
+}
+close(FD);
+
+if ($err == 0) {
+ print "No error found\n";
+}
diff --git a/usr.sbin/route6d/misc/cksum.c b/usr.sbin/route6d/misc/cksum.c
new file mode 100644
index 0000000..bff7e2d
--- /dev/null
+++ b/usr.sbin/route6d/misc/cksum.c
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <stdio.h>
+
+unsigned short buf[BUFSIZ];
+
+main()
+{
+ int i;
+ unsigned short *p = buf, *q = &buf[4];
+ unsigned long sum, sum2;
+
+ while (scanf("%x", &i) != EOF) {
+ *p++ = i; printf("%d ", i);
+ }
+ printf("\n");
+
+ sum = buf[2] + (buf[3] >> 8) & 0xff;
+ while (q != p)
+ sum += (*q++ & 0xffff);
+ sum2 = (sum & 0xffff) + (sum >> 16) & 0xffff;
+ printf("%x, %x\n", sum, sum2);
+}
diff --git a/usr.sbin/route6d/route6d.8 b/usr.sbin/route6d/route6d.8
new file mode 100644
index 0000000..5ccd611
--- /dev/null
+++ b/usr.sbin/route6d/route6d.8
@@ -0,0 +1,234 @@
+.\" Copyright (c) 1996 WIDE Project. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modifications, are permitted provided that the above copyright notice
+.\" and this paragraph are duplicated in all such forms and that any
+.\" documentation, advertising materials, and other materials related to
+.\" such distribution and use acknowledge that the software was developed
+.\" by the WIDE Project, Japan. The name of the Project may not be used to
+.\" endorse or promote products derived from this software without
+.\" specific prior written permission. THIS SOFTWARE IS PROVIDED ``AS IS''
+.\" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+.\" LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+.\" A PARTICULAR PURPOSE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd January 31, 1997
+.Dt ROUTE6D 8
+.Os KAME
+.Sh NAME
+.Nm route6d
+.Nd RIP6 Routing Daemon
+.Sh SYNOPSIS
+.Nm route6d
+.Op Fl adDhlqsS
+.Op Fl R Ar routelog
+.Op Fl A Ar prefix/preflen,if1[,if2...]
+.Op Fl L Ar prefix/preflen,if1[,if2...]
+.Op Fl N Ar if1[,if2...]
+.Op Fl O Ar prefix/preflen,if1[,if2...]
+.Op Fl T Ar if1[,if2...]
+.Op Fl t Ar tag
+.\"
+.Sh DESCRIPTION
+The
+.Nm
+is a routing daemon which supports RIP over IPv6.
+.Pp
+Options are:
+.Bl -tag -width indent
+.\"
+.It Fl a
+Enables aging of the statically defined routes.
+With this option, any
+statically defined routes will be removed unless corresponding updates
+arrive as if the routes are received at the startup of
+.Nm route6d .
+.\"
+.It Fl R Ar routelog
+This option makes the
+.Nm
+to log the route change (add/delete) to the file
+.Ar routelog .
+.\"
+.It Fl A Ar prefix/preflen,if1[,if2...]
+This option is used for aggregating routes.
+.Ar prefix/preflen
+specifies the prefix and the prefix length of the
+aggregated route.
+When advertising routes,
+.Nm
+filters specific routes covered by the aggregate,
+and advertises the aggregated route
+.Ar prefix/preflen ,
+to the interfaces specified in the comma-separated interface list,
+.Ar if1[,if2...] .
+.Nm
+creates a static route to
+.Ar prefix/preflen
+with
+.Dv RTF_REJECT
+flag, into the kernel routing table.
+.\"
+.It Fl d
+Enables output of debugging message.
+This option also instructs
+.Nm
+to run in foreground mode
+.Pq does not become daemon .
+.\"
+.It Fl D
+Enables extensive output of debugging message.
+This option also instructs
+.Nm
+to run in foreground mode
+.Pq does not become daemon .
+.\"
+.It Fl h
+Disables the split horizon processing.
+.\"
+.It Fl l
+By default,
+.Nm
+will not exchange site local routes for safety reasons.
+This is because semantics of site local address space is rather vague,
+and there is no good way to define site local boundary.
+With
+.Fl l
+option,
+.Nm
+will exchange site local routes as well.
+It must not be used on site boundary routers,
+since
+.Fl l
+option assumes that all interfaces are in the same site.
+.\"
+.It Fl L Ar prefix/preflen,if1[,if2...]
+Filter incoming routes from interfaces
+.Ar if1,[if2...] .
+.Nm
+will accept incoming routes that are in
+.Ar prefix/preflen .
+If multiple
+.Fl L
+options are specified, any routes that match one of the options is accepted.
+.Li ::/0
+is treated specially as default route, not
+.Do
+any route that has longer prefix length than, or equal to 0
+.Dc .
+If you would like to accept any route, specify no
+.Fl L
+option.
+For example, with
+.Do
+.Fl L
+.Li 3ffe::/16,if1
+.Fl L
+.Li ::/0,if1
+.Dc
+.Nm
+will accept default route and routes in 6bone test address, but no others.
+.\"
+.It Fl N Ar if1[,if2...]
+Do not listen to, or advertise, route from/to interfaces specified by
+.Ar if1,[if2...] .
+.\"
+.It Fl O Ar prefix/preflen,if1[,if2...]
+Restrict route advertisement toward interfaces specified by
+.Ar if1,[if2...] .
+With this option
+.Nm
+will only advertise routes that matches
+.Ar prefix/preflen .
+.\"
+.It Fl q
+Makes
+.Nm
+in listen-only mode.
+No advertisement is sent.
+.\"
+.It Fl s
+Makes
+.Nm
+to advertise the statically defined routes which exist in the kernel routing
+table when
+.Nm
+invoked.
+Announcements obey the regular split horizon rule.
+.\"
+.It Fl S
+This option is the same as
+.Fl s
+option except that no split horizon rule does apply.
+.\"
+.It Fl T Ar if1[,if2...]
+Advertise only default route, toward
+.Ar if1,[if2...] .
+.\"
+.It Fl t Ar tag
+Attach route tag
+.Ar tag
+to originated route entries.
+.Ar tag
+can be decimal, octal prefixed by
+.Li 0 ,
+or hexadecimal prefixed by
+.Li 0x .
+.\"
+.El
+.Pp
+Upon receipt of signal
+.Dv SIGINT
+or
+.Dv SIGUSR1 ,
+.Nm
+will dump the current internal state into
+.Pa /var/tmp/route6d_dump .
+.\"
+.Sh FILES
+.Bl -tag -width /var/tmp/route6d_dump -compact
+.It Pa /var/tmp/route6d_dump
+dumps internal state on
+.Dv SIGINT
+or
+.Dv SIGUSR1
+.El
+.\"
+.Sh SEE ALSO
+.Rs
+.%A G. Malkin
+.%A R. Minnear
+.%T RIPng for IPv6
+.%R RFC2080
+.%D January 1997
+.Re
+.\"
+.Sh NOTE
+.Nm Route6d
+uses IPv6 advanced API,
+defined in RFC2292,
+for communicating with peers using link-local addresses.
+.Pp
+Internally
+.Nm
+embeds interface identifier into bit 32 to 63 of link-local addresses
+.Po
+.Li fe80::xx
+and
+.Li ff02::xx
+.Pc
+so they will be visible on internal state dump file
+.Pq Pa /var/tmp/route6d_dump .
+.Pp
+Routing table manipulation differs from IPv6 implementation to implementation.
+Currently
+.Nm
+obeys WIDE Hydrangea/KAME IPv6 kernel,
+and will not be able to run on other platforms.
+.Pp
+Current
+.Nm
+does not reduce the rate of the triggered updates when consecutive updates
+arrive.
diff --git a/usr.sbin/route6d/route6d.c b/usr.sbin/route6d/route6d.c
new file mode 100644
index 0000000..857f5cb
--- /dev/null
+++ b/usr.sbin/route6d/route6d.c
@@ -0,0 +1,2913 @@
+/*
+ * $Header: /cvsroot/kame/kame/kame/kame/route6d/route6d.c,v 1.6 1999/09/10 08:20:59 itojun Exp $
+ */
+
+/*
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef lint
+static char _rcsid[] = "$Id: route6d.c,v 1.6 1999/09/10 08:20:59 itojun Exp $";
+#endif
+
+#include <stdio.h>
+
+#include <time.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+#include <syslog.h>
+#include <stddef.h>
+#include <err.h>
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/sysctl.h>
+#include <sys/errno.h>
+#ifdef ADVAPI
+#include <sys/uio.h>
+#endif
+#include <net/if.h>
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+#include <net/if_var.h>
+#endif /* __FreeBSD__ >= 3 */
+#define KERNEL 1
+#include <net/route.h>
+#undef KERNEL
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/ip6.h>
+#include <netinet/udp.h>
+#include <netdb.h>
+
+#include <arpa/inet.h>
+
+#include "route6d.h"
+
+#define MAXFILTER 40
+
+#ifdef DEBUG
+#define INIT_INTERVAL6 6
+#else
+#define INIT_INTERVAL6 10 /* Wait to submit a initial riprequest */
+#endif
+
+/* alignment constraint for routing socket */
+#define ROUNDUP(a) \
+ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
+#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
+
+/*
+ * Following two macros are highly depending on KAME Release
+ */
+#define IN6_LINKLOCAL_IFINDEX(addr) \
+ ((addr).s6_addr[2] << 8 | (addr).s6_addr[3])
+
+#define SET_IN6_LINKLOCAL_IFINDEX(addr, index) \
+ do { \
+ (addr).s6_addr[2] = ((index) >> 8) & 0xff; \
+ (addr).s6_addr[3] = (index) & 0xff; \
+ } while (0)
+
+struct ifc { /* Configuration of an interface */
+ char *ifc_name; /* if name */
+ struct ifc *ifc_next;
+ int ifc_index; /* if index */
+ int ifc_mtu; /* if mtu */
+ int ifc_metric; /* if metric */
+ short ifc_flags; /* flags */
+ struct in6_addr ifc_mylladdr; /* my link-local address */
+ struct sockaddr_in6 ifc_ripsin; /* rip multicast address */
+ struct iff *ifc_filter; /* filter structure */
+ struct ifac *ifc_addr; /* list of AF_INET6 addresses */
+ int ifc_joined; /* joined to ff02::9 */
+};
+
+struct ifac { /* Adddress associated to an interface */
+ struct ifc *ifa_conf; /* back pointer */
+ struct ifac *ifa_next;
+ struct in6_addr ifa_addr; /* address */
+ struct in6_addr ifa_raddr; /* remote address, valid in p2p */
+ int ifa_plen; /* prefix length */
+};
+
+struct iff {
+ int iff_type;
+ struct in6_addr iff_addr;
+ int iff_plen;
+ struct iff *iff_next;
+};
+
+struct ifc *ifc;
+int nifc; /* number of valid ifc's */
+struct ifc **index2ifc;
+int nindex2ifc;
+struct ifc *loopifcp = NULL; /* pointing to loopback */
+int loopifindex = 0; /* ditto */
+fd_set sockvec; /* vector to select() for receiving */
+int rtsock; /* the routing socket */
+int ripsock; /* socket to send/receive RIP datagram */
+
+struct rip6 *ripbuf; /* packet buffer for sending */
+
+/*
+ * Maintain the routes in a linked list. When the number of the routes
+ * grows, somebody would like to introduce a hash based or a radix tree
+ * based strucutre. I believe the number of routes handled by RIP is
+ * limited and I don't have to manage a complex data structure, however.
+ *
+ * One of the major drawbacks of the linear linked list is the difficulty
+ * of representing the relationship between a couple of routes. This may
+ * be a significant problem when we have to support route aggregation with
+ * supressing the specifices covered by the aggregate.
+ */
+
+struct riprt {
+ struct riprt *rrt_next; /* next destination */
+ struct riprt *rrt_same; /* same destination - future use */
+ struct netinfo6 rrt_info; /* network info */
+ struct in6_addr rrt_gw; /* gateway */
+ u_long rrt_flags;
+ time_t rrt_t; /* when the route validated */
+ int rrt_index; /* ifindex from which this route got */
+};
+
+struct riprt *riprt = 0;
+
+int dflag = 0; /* debug flag */
+int qflag = 0; /* quiet flag */
+int nflag = 0; /* don't update kernel routing table */
+int aflag = 0; /* age out even the statically defined routes */
+int hflag = 0; /* don't split horizon */
+int lflag = 0; /* exchange site local routes */
+int sflag = 0; /* announce static routes w/ split horizon */
+int Sflag = 0; /* announce static routes to every interface */
+int routetag = 0; /* route tag attached on originating case */
+
+char *filter[MAXFILTER];
+int filtertype[MAXFILTER];
+int nfilter = 0;
+
+pid_t pid;
+
+struct sockaddr_storage ripsin;
+
+struct rtentry rtentry;
+
+int interval = 1;
+time_t nextalarm = 0;
+time_t sup_trig_update = 0;
+
+FILE *rtlog = NULL;
+
+int logopened = 0;
+
+static u_long seq = 0;
+
+#define RTF_AGGREGATE 0x08000000
+#define RTF_NOADVERTISE 0x10000000
+#define RTF_NH_NOT_LLADDR 0x20000000
+#define RTF_SENDANYWAY 0x40000000
+#define RTF_CHANGED 0x80000000
+#define RTF_ROUTE_H 0xffff
+
+extern int errno;
+
+int main __P((int, char **));
+void ripalarm __P((int));
+void riprecv __P((void));
+void ripsend __P((struct ifc *, struct sockaddr_in6 *, int));
+void init __P((void));
+void sockopt __P((struct ifc *));
+void ifconfig __P((void));
+void ifconfig1 __P((struct ifreq *, struct ifc *, int));
+void rtrecv __P((void));
+int rt_del __P((const struct sockaddr_in6 *, const struct sockaddr_in6 *,
+ const struct sockaddr_in6 *));
+int rt_deladdr __P((struct ifc *, const struct sockaddr_in6 *,
+ const struct sockaddr_in6 *));
+void filterconfig __P((void));
+int getifmtu __P((int));
+const char *rttypes __P((struct rt_msghdr *rtm));
+const char *rtflags __P((struct rt_msghdr *rtm));
+const char *ifflags __P((int flags));
+void ifrt __P((struct ifc *, int));
+void applymask __P((struct in6_addr *, struct in6_addr *));
+void applyplen __P((struct in6_addr *, int));
+void ifrtdump __P((int));
+void ifdump __P((int));
+void ifdump0 __P((FILE *, const struct ifc *));
+void rtdump __P((int));
+void rt_entry __P((struct rt_msghdr *, int));
+void rtdexit __P((int));
+void riprequest __P((struct ifc *, struct netinfo6 *, int, struct sockaddr_in6 *));
+void ripflush __P((struct ifc *, struct sockaddr_in6 *));
+void sendrequest __P((struct ifc *));
+int mask2len __P((const struct in6_addr *, int));
+int sendpacket __P((struct sockaddr_in6 *, int));
+int addroute __P((struct riprt *, const struct in6_addr *, struct ifc *));
+int delroute __P((struct netinfo6 *, struct in6_addr *));
+struct in6_addr *getroute __P((struct netinfo6 *, struct in6_addr *));
+void krtread __P((int));
+int tobeadv __P((struct riprt *, struct ifc *));
+char *allocopy __P((char *));
+char *hms __P((void));
+const char *inet6_n2p __P((const struct in6_addr *));
+struct ifac *ifa_match __P((const struct ifc *, const struct in6_addr *, int));
+struct in6_addr *plen2mask __P((int));
+struct riprt *rtsearch __P((struct netinfo6 *));
+int ripinterval __P((int));
+time_t ripsuptrig __P((void));
+void fatal __P((const char *, ...));
+void trace __P((int, const char *, ...));
+void tracet __P((int, const char *, ...));
+unsigned int if_maxindex __P((void));
+struct ifc *ifc_find __P((char *));
+struct iff *iff_find __P((struct ifc *, int));
+void setindex2ifc __P((int, struct ifc *));
+
+#define MALLOC(type) ((type *)malloc(sizeof(type)))
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int ch;
+ int error = 0;
+ struct ifc *ifcp;
+ sigset_t mask, omask;
+ FILE *pidfile;
+ extern char *optarg;
+ extern int optind;
+ char *progname;
+
+ progname = strrchr(*argv, '/');
+ if (progname)
+ progname++;
+ else
+ progname = *argv;
+
+ pid = getpid();
+ while ((ch = getopt(argc, argv, "A:N:O:R:T:L:t:adDhlnqsS")) != EOF) {
+ switch (ch) {
+ case 'A':
+ case 'N':
+ case 'O':
+ case 'T':
+ case 'L':
+ if (nfilter >= MAXFILTER)
+ fatal("Exceeds MAXFILTER");
+ filtertype[nfilter] = ch;
+ filter[nfilter++] = allocopy(optarg);
+ break;
+ case 't':
+ sscanf(optarg, "%i", &routetag);
+ if (routetag & ~0xffff) {
+ fatal("invalid route tag");
+ /*NOTREACHED*/
+ }
+ break;
+ case 'R':
+ if ((rtlog = fopen(optarg, "w")) == NULL)
+ fatal("Can not write to routelog");
+ break;
+#define FLAG(c, flag, n) case c: flag = n; break
+ FLAG('a', aflag, 1);
+ FLAG('d', dflag, 1);
+ FLAG('D', dflag, 2);
+ FLAG('h', hflag, 1);
+ FLAG('l', lflag, 1);
+ FLAG('n', nflag, 1);
+ FLAG('q', qflag, 1);
+ FLAG('s', sflag, 1);
+ FLAG('S', Sflag, 1);
+#undef FLAG
+ default:
+ fatal("Invalid option specified, terminating");
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc > 0)
+ fatal("bogus extra arguments");
+
+ if (geteuid()) {
+ nflag = 1;
+ fprintf(stderr, "No kernel update is allowed\n");
+ }
+ openlog(progname, LOG_NDELAY|LOG_PID, LOG_DAEMON);
+ logopened++;
+ init();
+ ifconfig();
+ for (ifcp = ifc; ifcp; ifcp = ifcp->ifc_next) {
+ if (ifcp->ifc_index < 0) {
+ fprintf(stderr,
+"No ifindex found at %s (no link-local address?)\n",
+ ifcp->ifc_name);
+ error++;
+ }
+ }
+ if (error)
+ exit(1);
+ if (loopifcp == NULL)
+ fatal("No loopback found");
+ loopifindex = loopifcp->ifc_index;
+ for (ifcp = ifc; ifcp; ifcp = ifcp->ifc_next)
+ ifrt(ifcp, 0);
+ filterconfig();
+ krtread(0);
+ if (dflag)
+ ifrtdump(0);
+
+ if (dflag == 0) {
+#if 1
+ if (daemon(0, 0) < 0)
+ fatal("daemon");
+#else
+ if (fork())
+ exit(0);
+ if (setsid() < 0)
+ fatal("setid");
+#endif
+ }
+ pid = getpid();
+ if ((pidfile = fopen(ROUTE6D_PID, "w")) != NULL) {
+ fprintf(pidfile, "%d\n", pid);
+ fclose(pidfile);
+ }
+
+ if ((ripbuf = (struct rip6 *)malloc(RIP6_MAXMTU)) == NULL)
+ fatal("malloc");
+ ripbuf->rip6_cmd = RIP6_RESPONSE;
+ ripbuf->rip6_vers = RIP6_VERSION;
+ ripbuf->rip6_res1[0] = 0;
+ ripbuf->rip6_res1[1] = 0;
+
+ if (signal(SIGALRM, ripalarm) == SIG_ERR)
+ fatal("signal: SIGALRM");
+ if (signal(SIGQUIT, rtdexit) == SIG_ERR)
+ fatal("signal: SIGQUIT");
+ if (signal(SIGTERM, rtdexit) == SIG_ERR)
+ fatal("signal: SIGTERM");
+ if (signal(SIGUSR1, ifrtdump) == SIG_ERR)
+ fatal("signal: SIGUSR1");
+ if (signal(SIGHUP, ifrtdump) == SIG_ERR)
+ fatal("signal: SIGHUP");
+ if (signal(SIGINT, ifrtdump) == SIG_ERR)
+ fatal("signal: SIGINT");
+ /*
+ * To avoid rip packet congestion (not on a cable but in this
+ * process), wait for a moment to send the first RIP6_RESPONSE
+ * packets.
+ */
+ alarm(ripinterval(INIT_INTERVAL6));
+
+ for (ifcp = ifc; ifcp; ifcp = ifcp->ifc_next) {
+ if (ifcp->ifc_index > 0 && (ifcp->ifc_flags & IFF_UP))
+ sendrequest(ifcp);
+ }
+
+ syslog(LOG_INFO, "**** Started ****");
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGALRM);
+ while (1) {
+ fd_set recvec;
+
+ FD_COPY(&sockvec, &recvec);
+ switch (select(FD_SETSIZE, &recvec, 0, 0, 0)) {
+ case -1:
+ if (errno == EINTR)
+ continue;
+ fatal("select");
+ case 0:
+ continue;
+ default:
+ if (FD_ISSET(ripsock, &recvec)) {
+ sigprocmask(SIG_BLOCK, &mask, &omask);
+ riprecv();
+ sigprocmask(SIG_SETMASK, &omask, NULL);
+ }
+ if (FD_ISSET(rtsock, &recvec)) {
+ sigprocmask(SIG_BLOCK, &mask, &omask);
+ rtrecv();
+ sigprocmask(SIG_SETMASK, &omask, NULL);
+ }
+ }
+ }
+}
+
+/*
+ * gracefully exits after resetting sockopts.
+ */
+/* ARGSUSED */
+void
+rtdexit(sig)
+ int sig;
+{
+ struct riprt *rrt;
+
+ alarm(0);
+ for (rrt = riprt; rrt; rrt = rrt->rrt_next) {
+ if (rrt->rrt_flags & RTF_AGGREGATE) {
+ delroute(&rrt->rrt_info, &rrt->rrt_gw);
+ }
+ }
+ close(ripsock);
+ close(rtsock);
+ syslog(LOG_INFO, "**** Terminated ****");
+ closelog();
+ exit(1);
+}
+
+/*
+ * Called periodically:
+ * 1. age out the learned route. remove it if necessary.
+ * 2. submit RIP6_RESPONSE packets.
+ * Invoked in every SUPPLY_INTERVAL6 (30) seconds. I believe we don't have
+ * to invoke this function in every 1 or 5 or 10 seconds only to age the
+ * routes more precisely.
+ */
+/* ARGSUSED */
+void
+ripalarm(sig)
+ int sig;
+{
+ struct ifc *ifcp;
+ struct riprt *rrt, *rrt_prev, *rrt_next;
+ time_t t_lifetime, t_holddown;
+
+ /* age the RIP routes */
+ rrt_prev = 0;
+ t_lifetime = time(NULL) - RIP_LIFETIME;
+ t_holddown = t_lifetime - RIP_HOLDDOWN;
+ for (rrt = riprt; rrt; rrt = rrt_next) {
+ rrt_next = rrt->rrt_next;
+
+ if (rrt->rrt_t == 0) {
+ rrt_prev = rrt;
+ continue;
+ }
+ if (rrt->rrt_t < t_holddown) {
+ if (rrt_prev) {
+ rrt_prev->rrt_next = rrt->rrt_next;
+ } else {
+ riprt = rrt->rrt_next;
+ }
+ delroute(&rrt->rrt_info, &rrt->rrt_gw);
+ free(rrt);
+ continue;
+ }
+ if (rrt->rrt_t < t_lifetime)
+ rrt->rrt_info.rip6_metric = HOPCNT_INFINITY6;
+ rrt_prev = rrt;
+ }
+ /* Supply updates */
+ for (ifcp = ifc; ifcp; ifcp = ifcp->ifc_next) {
+ if (ifcp->ifc_index > 0 && (ifcp->ifc_flags & IFF_UP))
+ ripsend(ifcp, &ifcp->ifc_ripsin, 0);
+ }
+ alarm(ripinterval(SUPPLY_INTERVAL6));
+}
+
+void
+init()
+{
+#ifdef ADVAPI
+ int i;
+#endif
+ int int0, int255, error;
+ struct addrinfo hints, *res;
+ char port[10];
+
+ ifc = (struct ifc *)NULL;
+ nifc = 0;
+ nindex2ifc = 0; /*initial guess*/
+ index2ifc = NULL;
+ snprintf(port, sizeof(port), "%d", RIP6_PORT);
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_INET6;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_flags = AI_PASSIVE;
+ error = getaddrinfo(NULL, port, &hints, &res);
+ if (error)
+ fatal(gai_strerror(error));
+ if (res->ai_next)
+ fatal(":: resolved to multiple address");
+
+ int0 = 0; int255 = 255;
+ ripsock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+ if (ripsock < 0)
+ fatal("rip socket");
+ if (bind(ripsock, res->ai_addr, res->ai_addrlen) < 0)
+ fatal("rip bind");
+ if (setsockopt(ripsock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
+ &int255, sizeof(int255)) < 0)
+ fatal("rip IPV6_MULTICAST_HOPS");
+ if (setsockopt(ripsock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
+ &int0, sizeof(int0)) < 0)
+ fatal("rip IPV6_MULTICAST_LOOP");
+#ifdef ADVAPI
+ i = 1;
+ if (setsockopt(ripsock, IPPROTO_IPV6, IPV6_PKTINFO, &i, sizeof(i)) < 0)
+ fatal("rip IPV6_PKTINFO");
+#endif /*ADVAPI*/
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_INET6;
+ hints.ai_socktype = SOCK_DGRAM;
+ error = getaddrinfo(RIP6_DEST, port, &hints, &res);
+ if (error)
+ fatal(gai_strerror(error));
+ if (res->ai_next)
+ fatal("%s resolved to multiple address", RIP6_DEST);
+ memcpy(&ripsin, res->ai_addr, res->ai_addrlen);
+
+#ifdef FD_ZERO
+ FD_ZERO(&sockvec);
+#else
+ memset(&sockvec, 0, sizeof(sockvec));
+#endif
+ FD_SET(ripsock, &sockvec);
+
+ if (nflag == 0) {
+ if ((rtsock = socket(PF_ROUTE, SOCK_RAW, 0)) < 0)
+ fatal("route socket");
+ FD_SET(rtsock, &sockvec);
+ } else
+ rtsock = -1; /*just for safety */
+}
+
+#define RIPSIZE(n) (sizeof(struct rip6) + (n-1) * sizeof(struct netinfo6))
+
+/*
+ * ripflush flushes the rip datagram stored in the rip buffer
+ */
+static int nrt;
+static struct netinfo6 *np;
+
+void
+ripflush(ifcp, sin)
+ struct ifc *ifcp;
+ struct sockaddr_in6 *sin;
+{
+ int i;
+ int error;
+
+ if (ifcp)
+ tracet(1, "Send(%s): info(%d) to %s.%d\n",
+ ifcp->ifc_name, nrt,
+ inet6_n2p(&sin->sin6_addr), ntohs(sin->sin6_port));
+ else
+ tracet(1, "Send: info(%d) to %s.%d\n",
+ nrt, inet6_n2p(&sin->sin6_addr), ntohs(sin->sin6_port));
+ if (dflag >= 2) {
+ np = ripbuf->rip6_nets;
+ for (i = 0; i < nrt; i++, np++) {
+ if (np->rip6_metric == NEXTHOP_METRIC) {
+ if (IN6_IS_ADDR_UNSPECIFIED(&np->rip6_dest))
+ trace(2, " NextHop reset");
+ else {
+ trace(2, " NextHop %s",
+ inet6_n2p(&np->rip6_dest));
+ }
+ } else {
+ trace(2, " %s/%d[%d]",
+ inet6_n2p(&np->rip6_dest),
+ np->rip6_plen, np->rip6_metric);
+ }
+ if (np->rip6_tag) {
+ trace(2, " tag=0x%04x",
+ ntohs(np->rip6_tag) & 0xffff);
+ }
+ trace(2, "\n");
+ }
+ }
+ error = sendpacket(sin, RIPSIZE(nrt));
+ if (error == EAFNOSUPPORT) {
+ /* Protocol not supported */
+ tracet(1, "Could not send info to %s (%s): "
+ "set IFF_UP to 0\n",
+ ifcp->ifc_name, inet6_n2p(&ifcp->ifc_ripsin.sin6_addr));
+ ifcp->ifc_flags &= ~IFF_UP; /* As if down for AF_INET6 */
+ }
+ nrt = 0; np = ripbuf->rip6_nets;
+}
+
+/*
+ * Generate RIP6_RESPONSE packets and send them.
+ */
+void
+ripsend(ifcp, sin, flag)
+ struct ifc *ifcp;
+ struct sockaddr_in6 *sin;
+ int flag;
+{
+ struct riprt *rrt;
+ struct in6_addr *nh; /* next hop */
+ struct in6_addr ia;
+ struct iff *iffp;
+ int maxrte, ok;
+
+ if (ifcp == NULL) {
+ /*
+ * Request from non-link local address is not
+ * a regular route6d update.
+ */
+ maxrte = (IFMINMTU - sizeof(struct ip6_hdr) -
+ sizeof(struct udphdr) -
+ sizeof(struct rip6) + sizeof(struct netinfo6)) /
+ sizeof(struct netinfo6);
+ nrt = 0; np = ripbuf->rip6_nets; nh = NULL;
+ for (rrt = riprt; rrt; rrt = rrt->rrt_next) {
+ if (rrt->rrt_flags & RTF_NOADVERTISE)
+ continue;
+ /* Put the route to the buffer */
+ *np = rrt->rrt_info;
+ np++; nrt++;
+ if (nrt == maxrte) {
+ ripflush(NULL, sin);
+ nh = NULL;
+ }
+ }
+ if (nrt) /* Send last packet */
+ ripflush(NULL, sin);
+ return;
+ }
+
+ if ((flag & RTF_SENDANYWAY) == 0 &&
+ (qflag || (ifcp->ifc_flags & IFF_LOOPBACK)))
+ return;
+ if (iff_find(ifcp, 'N') != NULL)
+ return;
+ if (iff_find(ifcp, 'T') != NULL) {
+ struct netinfo6 rrt_info;
+ memset(&rrt_info, 0, sizeof(struct netinfo6));
+ rrt_info.rip6_dest = in6addr_any;
+ rrt_info.rip6_plen = 0;
+ rrt_info.rip6_metric = 1;
+ rrt_info.rip6_tag = htons(routetag & 0xffff);
+ np = ripbuf->rip6_nets;
+ *np = rrt_info;
+ nrt = 1;
+ ripflush(ifcp, sin);
+ return;
+ }
+ maxrte = (ifcp->ifc_mtu - sizeof(struct ip6_hdr) -
+ sizeof(struct udphdr) -
+ sizeof(struct rip6) + sizeof(struct netinfo6)) /
+ sizeof(struct netinfo6);
+ nrt = 0; np = ripbuf->rip6_nets; nh = NULL;
+ for (rrt = riprt; rrt; rrt = rrt->rrt_next) {
+ if (rrt->rrt_flags & RTF_NOADVERTISE)
+ continue;
+ /* Need to check filer here */
+ ok = 1;
+ for (iffp = ifcp->ifc_filter; iffp; iffp = iffp->iff_next) {
+ if (iffp->iff_type != 'A')
+ continue;
+ if (rrt->rrt_info.rip6_plen <= iffp->iff_plen)
+ continue;
+ ia = rrt->rrt_info.rip6_dest;
+ applyplen(&ia, iffp->iff_plen);
+ if (IN6_ARE_ADDR_EQUAL(&ia, &iffp->iff_addr)) {
+ ok = 0;
+ break;
+ }
+ }
+ if (!ok)
+ continue;
+ for (iffp = ifcp->ifc_filter; iffp; iffp = iffp->iff_next) {
+ if (iffp->iff_type != 'O')
+ continue;
+ ok = 0;
+ if (rrt->rrt_info.rip6_plen < iffp->iff_plen)
+ continue;
+ ia = rrt->rrt_info.rip6_dest;
+ applyplen(&ia, iffp->iff_plen);
+ if (IN6_ARE_ADDR_EQUAL(&ia, &iffp->iff_addr)) {
+ ok = 1;
+ break;
+ }
+ }
+ if (!ok)
+ continue;
+ /* Check split horizon and other conditions */
+ if (tobeadv(rrt, ifcp) == 0)
+ continue;
+ /* Only considers the routes with flag if specified */
+ if ((flag & RTF_CHANGED) && (rrt->rrt_flags & RTF_CHANGED) == 0)
+ continue;
+ /* Check nexthop */
+ if (rrt->rrt_index == ifcp->ifc_index &&
+ !IN6_IS_ADDR_UNSPECIFIED(&rrt->rrt_gw) &&
+ (rrt->rrt_flags & RTF_NH_NOT_LLADDR) == 0) {
+ if (nh == NULL || !IN6_ARE_ADDR_EQUAL(nh, &rrt->rrt_gw)) {
+ if (nrt == maxrte - 2)
+ ripflush(ifcp, sin);
+ np->rip6_dest = rrt->rrt_gw;
+ if (IN6_IS_ADDR_LINKLOCAL(&np->rip6_dest))
+ SET_IN6_LINKLOCAL_IFINDEX(np->rip6_dest, 0);
+ np->rip6_plen = 0;
+ np->rip6_tag = 0;
+ np->rip6_metric = NEXTHOP_METRIC;
+ nh = &rrt->rrt_gw;
+ np++; nrt++;
+ }
+ } else if (nh && (rrt->rrt_index != ifcp->ifc_index ||
+ !IN6_ARE_ADDR_EQUAL(nh, &rrt->rrt_gw) ||
+ rrt->rrt_flags & RTF_NH_NOT_LLADDR)) {
+ /* Reset nexthop */
+ if (nrt == maxrte - 2)
+ ripflush(ifcp, sin);
+ memset(np, 0, sizeof(struct netinfo6));
+ np->rip6_metric = NEXTHOP_METRIC;
+ nh = NULL;
+ np++; nrt++;
+ }
+ /* Put the route to the buffer */
+ *np = rrt->rrt_info;
+ np++; nrt++;
+ if (nrt == maxrte) {
+ ripflush(ifcp, sin);
+ nh = NULL;
+ }
+ }
+ if (nrt) /* Send last packet */
+ ripflush(ifcp, sin);
+}
+
+/*
+ * Determine if the route is to be advertised on the specified interface.
+ * It checks options specified in the arguments and the split horizon rule.
+ */
+int
+tobeadv(rrt, ifcp)
+ struct riprt *rrt;
+ struct ifc *ifcp;
+{
+
+ /* Special care for static routes */
+ if (rrt->rrt_flags & RTF_STATIC) {
+ if (Sflag) /* Yes, advertise it anyway */
+ return 1;
+ if (sflag && rrt->rrt_index != ifcp->ifc_index)
+ return 1;
+ return 0;
+ }
+ /* Regular split horizon */
+ if (hflag == 0 && rrt->rrt_index == ifcp->ifc_index)
+ return 0;
+ return 1;
+}
+
+/*
+ * Send a rip packet actually.
+ */
+int
+sendpacket(sin, len)
+ struct sockaddr_in6 *sin;
+ int len;
+{
+ /*
+ * MSG_DONTROUTE should not be specified when it responds with a
+ * RIP6_REQUEST message. SO_DONTROUTE has been specified to
+ * other sockets.
+ */
+#ifdef ADVAPI
+ struct msghdr m;
+ struct cmsghdr *cm;
+ struct iovec iov[2];
+ u_char cmsgbuf[256];
+ struct in6_pktinfo *pi;
+ int index;
+ struct sockaddr_in6 sincopy;
+
+ /* do not overwrite the given sin */
+ sincopy = *sin;
+ sin = &sincopy;
+
+ if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr)
+ || IN6_IS_ADDR_MULTICAST(&sin->sin6_addr)) {
+ index = IN6_LINKLOCAL_IFINDEX(sin->sin6_addr);
+ SET_IN6_LINKLOCAL_IFINDEX(sin->sin6_addr, 0);
+ } else
+ index = 0;
+
+ m.msg_name = (caddr_t)sin;
+ m.msg_namelen = sizeof(*sin);
+ iov[0].iov_base = (caddr_t)ripbuf;
+ iov[0].iov_len = len;
+ m.msg_iov = iov;
+ m.msg_iovlen = 1;
+ if (!index) {
+ m.msg_control = NULL;
+ m.msg_controllen = 0;
+ } else {
+ memset(cmsgbuf, 0, sizeof(cmsgbuf));
+ cm = (struct cmsghdr *)cmsgbuf;
+ m.msg_control = (caddr_t)cm;
+ m.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo));
+
+ cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
+ cm->cmsg_level = IPPROTO_IPV6;
+ cm->cmsg_type = IPV6_PKTINFO;
+ pi = (struct in6_pktinfo *)CMSG_DATA(cm);
+ memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr)); /*::*/
+ pi->ipi6_ifindex = index;
+ }
+
+ if (sendmsg(ripsock, &m, 0 /*MSG_DONTROUTE*/) < 0) {
+ trace(1, "sendmsg: %s\n", strerror(errno));
+ return errno;
+ }
+#else
+ if (sendto(ripsock, ripbuf, len, 0 /*MSG_DONTROUTE*/,
+ (struct sockaddr *)sin, sizeof(struct sockaddr_in6)) < 0) {
+ trace(1, "sendto: %s\n", strerror(errno));
+ return errno;
+ }
+#endif
+ return 0;
+}
+
+/*
+ * Receive and process RIP packets. Update the routes/kernel forwarding
+ * table if necessary.
+ */
+void
+riprecv()
+{
+ struct ifc *ifcp, *ic;
+ struct sockaddr_in6 fsock;
+ struct in6_addr nh; /* next hop */
+ struct rip6 *rp;
+ struct netinfo6 *np, *nq;
+ struct riprt *rrt;
+ int len, nn, need_trigger, index;
+#ifndef ADVAPI
+ int flen;
+#endif
+ char buf[4 * RIP6_MAXMTU];
+ time_t t;
+#ifdef ADVAPI
+ struct msghdr m;
+ struct cmsghdr *cm;
+ struct iovec iov[2];
+ u_char cmsgbuf[256];
+ struct in6_pktinfo *pi;
+#endif /*ADVAPI*/
+ struct iff *iffp;
+ struct in6_addr ia;
+ int ok;
+
+ need_trigger = 0;
+#ifdef ADVAPI
+ m.msg_name = (caddr_t)&fsock;
+ m.msg_namelen = sizeof(fsock);
+ iov[0].iov_base = (caddr_t)buf;
+ iov[0].iov_len = sizeof(buf);
+ m.msg_iov = iov;
+ m.msg_iovlen = 1;
+ cm = (struct cmsghdr *)cmsgbuf;
+ m.msg_control = (caddr_t)cm;
+ m.msg_controllen = sizeof(cmsgbuf);
+ if ((len = recvmsg(ripsock, &m, 0)) < 0)
+ fatal("recvmsg");
+ index = 0;
+ for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&m);
+ cm;
+ cm = (struct cmsghdr *)CMSG_NXTHDR(&m, cm)) {
+ if (cm->cmsg_level == IPPROTO_IPV6
+ && cm->cmsg_type == IPV6_PKTINFO) {
+ pi = (struct in6_pktinfo *)(CMSG_DATA(cm));
+ index = pi->ipi6_ifindex;
+ break;
+ }
+ }
+ if (index && IN6_IS_ADDR_LINKLOCAL(&fsock.sin6_addr))
+ SET_IN6_LINKLOCAL_IFINDEX(fsock.sin6_addr, index);
+#else
+ flen = sizeof(struct sockaddr_in6);
+ if ((len = recvfrom(ripsock, buf, sizeof(buf), 0,
+ (struct sockaddr *)&fsock, &flen)) < 0)
+ fatal("recvfrom");
+ if (IN6_IS_ADDR_LINKLOCAL(&fsock.sin6_addr))
+ index = IN6_LINKLOCAL_IFINDEX(fsock.sin6_addr);
+ else
+ index = 0;
+#endif /*ADVAPI*/
+
+ nh = fsock.sin6_addr;
+ nn = (len - sizeof(struct rip6) + sizeof(struct netinfo6)) /
+ sizeof(struct netinfo6);
+ rp = (struct rip6 *)buf;
+ np = rp->rip6_nets;
+
+ if (rp->rip6_vers != RIP6_VERSION) {
+ trace(1, "Incorrect RIP version %d\n", rp->rip6_vers);
+ return;
+ }
+ if (rp->rip6_cmd == RIP6_REQUEST) {
+ if (index && index < nindex2ifc) {
+ ifcp = index2ifc[index];
+ riprequest(ifcp, np, nn, &fsock);
+ } else {
+ riprequest(NULL, np, nn, &fsock);
+ }
+ return;
+ }
+
+ if (!IN6_IS_ADDR_LINKLOCAL(&fsock.sin6_addr)) {
+ trace(1, "Packets from non-ll addr: %s\n",
+ inet6_n2p(&fsock.sin6_addr));
+ return; /* Ignore packets from non-link-local addr */
+ }
+ index = IN6_LINKLOCAL_IFINDEX(fsock.sin6_addr);
+ ifcp = (index < nindex2ifc) ? index2ifc[index] : NULL;
+ if (!ifcp) {
+ trace(1, "Packets to unknown interface index %d\n", index);
+ return; /* Ignore it */
+ }
+ if (IN6_ARE_ADDR_EQUAL(&ifcp->ifc_mylladdr, &fsock.sin6_addr))
+ return; /* The packet is from me; ignore */
+ if (rp->rip6_cmd != RIP6_RESPONSE) {
+ trace(1, "Invalid command %d\n", rp->rip6_cmd);
+ return;
+ }
+ if (iff_find(ifcp, 'N') != NULL)
+ return;
+ tracet(1, "Recv(%s): from %s.%d info(%d)\n",
+ ifcp->ifc_name, inet6_n2p(&nh), ntohs(fsock.sin6_port), nn);
+
+ t = time(NULL);
+ for (; nn; nn--, np++) {
+ if (np->rip6_metric == NEXTHOP_METRIC) {
+ /* modify neighbor address */
+ if (IN6_IS_ADDR_LINKLOCAL(&np->rip6_dest)) {
+ nh = np->rip6_dest;
+ SET_IN6_LINKLOCAL_IFINDEX(nh, index);
+ trace(1, "\tNexthop: %s\n", inet6_n2p(&nh));
+ } else if (IN6_IS_ADDR_UNSPECIFIED(&np->rip6_dest)) {
+ nh = fsock.sin6_addr;
+ trace(1, "\tNexthop: %s\n", inet6_n2p(&nh));
+ } else {
+ nh = fsock.sin6_addr;
+ trace(1, "\tInvalid Nexthop: %s\n",
+ inet6_n2p(&np->rip6_dest));
+ }
+ continue;
+ }
+ if (IN6_IS_ADDR_MULTICAST(&np->rip6_dest)) {
+ trace(1, "\tMulticast netinfo6: %s/%d [%d]\n",
+ inet6_n2p(&np->rip6_dest),
+ np->rip6_plen, np->rip6_metric);
+ continue;
+ }
+ if (IN6_IS_ADDR_LOOPBACK(&np->rip6_dest)) {
+ trace(1, "\tLoopback netinfo6: %s/%d [%d]\n",
+ inet6_n2p(&np->rip6_dest),
+ np->rip6_plen, np->rip6_metric);
+ continue;
+ }
+ if (IN6_IS_ADDR_LINKLOCAL(&np->rip6_dest)) {
+ trace(1, "\tLink Local netinfo6: %s/%d [%d]\n",
+ inet6_n2p(&np->rip6_dest),
+ np->rip6_plen, np->rip6_metric);
+ continue;
+ }
+ /* may need to pass sitelocal prefix in some case, however*/
+ if (IN6_IS_ADDR_SITELOCAL(&np->rip6_dest) && !lflag) {
+ trace(1, "\tSite Local netinfo6: %s/%d [%d]\n",
+ inet6_n2p(&np->rip6_dest),
+ np->rip6_plen, np->rip6_metric);
+ continue;
+ }
+ trace(2, "\tnetinfo6: %s/%d [%d]",
+ inet6_n2p(&np->rip6_dest),
+ np->rip6_plen, np->rip6_metric);
+ if (np->rip6_tag)
+ trace(2, " tag=0x%04x", ntohs(np->rip6_tag) & 0xffff);
+
+ /* Listen-only filter */
+ ok = 1; /* if there's no L filter, it is ok */
+ for (iffp = ifcp->ifc_filter; iffp; iffp = iffp->iff_next) {
+ if (iffp->iff_type != 'L')
+ continue;
+ ok = 0;
+ if (np->rip6_plen < iffp->iff_plen)
+ continue;
+ /* special rule: ::/0 means default, not "in /0" */
+ if (iffp->iff_plen == 0 && np->rip6_plen > 0)
+ continue;
+ ia = np->rip6_dest;
+ applyplen(&ia, iffp->iff_plen);
+ if (IN6_ARE_ADDR_EQUAL(&ia, &iffp->iff_addr)) {
+ ok = 1;
+ break;
+ }
+ }
+ if (!ok) {
+ trace(2, " (filtered)\n");
+ continue;
+ }
+
+ trace(2, "\n");
+ np->rip6_metric++;
+ np->rip6_metric += ifcp->ifc_metric;
+ if (np->rip6_metric > HOPCNT_INFINITY6)
+ np->rip6_metric = HOPCNT_INFINITY6;
+
+ applyplen(&np->rip6_dest, np->rip6_plen);
+ if ((rrt = rtsearch(np)) != NULL) {
+ if (rrt->rrt_t == 0)
+ continue; /* Intf route has priority */
+ nq = &rrt->rrt_info;
+ if (nq->rip6_metric > np->rip6_metric) {
+ if (rrt->rrt_index == ifcp->ifc_index &&
+ IN6_ARE_ADDR_EQUAL(&nh, &rrt->rrt_gw)) {
+ /* Small metric from the same gateway */
+ nq->rip6_metric = np->rip6_metric;
+ } else {
+ /* Better route found */
+ rrt->rrt_index = ifcp->ifc_index;
+ /* Update routing table */
+ delroute(nq, &rrt->rrt_gw);
+ rrt->rrt_gw = nh;
+ *nq = *np;
+ addroute(rrt, &nh, ifcp);
+ }
+ rrt->rrt_flags |= RTF_CHANGED;
+ rrt->rrt_t = t;
+ need_trigger = 1;
+ } else if (nq->rip6_metric < np->rip6_metric &&
+ rrt->rrt_index == ifcp->ifc_index &&
+ IN6_ARE_ADDR_EQUAL(&nh, &rrt->rrt_gw)) {
+ /* Got worse route from same gw */
+ nq->rip6_metric = np->rip6_metric;
+ rrt->rrt_t = t;
+ rrt->rrt_flags |= RTF_CHANGED;
+ need_trigger = 1;
+ } else if (nq->rip6_metric == np->rip6_metric &&
+ rrt->rrt_index == ifcp->ifc_index &&
+ IN6_ARE_ADDR_EQUAL(&nh, &rrt->rrt_gw) &&
+ np->rip6_metric < HOPCNT_INFINITY6) {
+ /* same metric, same route from same gw */
+ rrt->rrt_t = t;
+ }
+ /*
+ * if nq->rip6_metric == HOPCNT_INFINITY6 then
+ * do not update age value. Do nothing.
+ */
+ } else if (np->rip6_metric < HOPCNT_INFINITY6) {
+ /* Got a new valid route */
+ if ((rrt = MALLOC(struct riprt)) == NULL)
+ fatal("malloc: struct riprt");
+ nq = &rrt->rrt_info;
+
+ rrt->rrt_same = NULL;
+ rrt->rrt_index = ifcp->ifc_index;
+ rrt->rrt_flags = RTF_UP|RTF_GATEWAY;
+ rrt->rrt_gw = nh;
+ *nq = *np;
+ applyplen(&nq->rip6_dest, nq->rip6_plen);
+ if (nq->rip6_plen == sizeof(struct in6_addr) * 8)
+ rrt->rrt_flags |= RTF_HOST;
+
+ /* Put the route to the list */
+ rrt->rrt_next = riprt;
+ riprt = rrt;
+ /* Update routing table */
+ addroute(rrt, &nh, ifcp);
+ rrt->rrt_flags |= RTF_CHANGED;
+ need_trigger = 1;
+ rrt->rrt_t = t;
+ }
+ }
+ /* XXX need to care the interval between triggered updates */
+ if (need_trigger) {
+ if (nextalarm > time(NULL) + RIP_TRIG_INT6_MAX) {
+ for (ic = ifc; ic; ic = ic->ifc_next) {
+ if (ifcp->ifc_index == ic->ifc_index)
+ continue;
+ if (ic->ifc_flags & IFF_UP)
+ ripsend(ic, &ic->ifc_ripsin,
+ RTF_CHANGED);
+ }
+ }
+ /* Reset the flag */
+ for (rrt = riprt; rrt; rrt = rrt->rrt_next)
+ rrt->rrt_flags &= ~RTF_CHANGED;
+ }
+}
+
+/*
+ * Send all routes request packet to the specified interface.
+ */
+void
+sendrequest(ifcp)
+ struct ifc *ifcp;
+{
+ struct netinfo6 *np;
+ int error;
+
+ if (ifcp->ifc_flags & IFF_LOOPBACK)
+ return;
+ ripbuf->rip6_cmd = RIP6_REQUEST;
+ np = ripbuf->rip6_nets;
+ memset(np, 0, sizeof(struct netinfo6));
+ np->rip6_metric = HOPCNT_INFINITY6;
+ tracet(1, "Send rtdump Request to %s (%s)\n",
+ ifcp->ifc_name, inet6_n2p(&ifcp->ifc_ripsin.sin6_addr));
+ error = sendpacket(&ifcp->ifc_ripsin, RIPSIZE(1));
+ if (error == EAFNOSUPPORT) {
+ /* Protocol not supported */
+ tracet(1, "Could not send rtdump Request to %s (%s): "
+ "set IFF_UP to 0\n",
+ ifcp->ifc_name, inet6_n2p(&ifcp->ifc_ripsin.sin6_addr));
+ ifcp->ifc_flags &= ~IFF_UP; /* As if down for AF_INET6 */
+ }
+ ripbuf->rip6_cmd = RIP6_RESPONSE;
+}
+
+/*
+ * Process a RIP6_REQUEST packet.
+ */
+void
+riprequest(ifcp, np, nn, sin)
+ struct ifc *ifcp;
+ struct netinfo6 *np;
+ int nn;
+ struct sockaddr_in6 *sin;
+{
+ int i;
+ struct riprt *rrt;
+
+ if (!(nn == 1 && IN6_IS_ADDR_UNSPECIFIED(&np->rip6_dest) &&
+ np->rip6_plen == 0 && np->rip6_metric == HOPCNT_INFINITY6)) {
+ /* Specific response, don't split-horizon */
+ trace(1, "\tRIP Request\n");
+ for (i = 0; i < nn; i++, np++) {
+ rrt = rtsearch(np);
+ if (rrt)
+ np->rip6_metric = rrt->rrt_info.rip6_metric;
+ else
+ np->rip6_metric = HOPCNT_INFINITY6;
+ }
+ (void)sendpacket(sin, RIPSIZE(nn));
+ return;
+ }
+ /* Whole routing table dump */
+ trace(1, "\tRIP Request -- whole routing table\n");
+ ripsend(ifcp, sin, RTF_SENDANYWAY);
+}
+
+/*
+ * Get information of each interface.
+ */
+void
+ifconfig()
+{
+ int s, i;
+ char *buf;
+ struct ifconf ifconf;
+ struct ifreq *ifrp, ifr;
+ struct ifc *ifcp;
+ struct ipv6_mreq mreq;
+ int bufsiz;
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
+ fatal("socket");
+
+ /* wild guess - v4, media, link, v6 * 3 */
+ bufsiz = if_maxindex() * sizeof(struct ifreq) * 6;
+ if ((buf = (char *)malloc(bufsiz)) == NULL)
+ fatal("malloc");
+
+ /*
+ * ioctl(SIOCGIFCONF) does not return error on buffer size.
+ * we'll try to guess the buffer size by trying it twice, with
+ * different buffer size.
+ */
+ ifconf.ifc_buf = buf;
+ ifconf.ifc_len = bufsiz / 2;
+ if (ioctl(s, SIOCGIFCONF, (char *)&ifconf) < 0)
+ fatal("ioctl: SIOCGIFCONF");
+ i = ifconf.ifc_len;
+ while (1) {
+ ifconf.ifc_buf = buf;
+ ifconf.ifc_len = bufsiz;
+ if (ioctl(s, SIOCGIFCONF, (char *)&ifconf) < 0)
+ fatal("ioctl: SIOCGIFCONF");
+ if (i == ifconf.ifc_len)
+ break;
+ i = ifconf.ifc_len;
+ bufsiz *= 2;
+ if ((buf = (char *)realloc(buf, bufsiz)) == NULL)
+ fatal("realloc");
+ }
+ for (i = 0; i < ifconf.ifc_len; ) {
+ ifrp = (struct ifreq *)(buf + i);
+ if (ifrp->ifr_addr.sa_family != AF_INET6)
+ goto skip;
+ ifcp = ifc_find(ifrp->ifr_name);
+ strcpy(ifr.ifr_name, ifrp->ifr_name);
+ if (ioctl(s, SIOCGIFFLAGS, (char *)&ifr) < 0)
+ fatal("ioctl: SIOCGIFFLAGS");
+ /* we are interested in multicast-capable interfaces */
+ if ((ifr.ifr_flags & IFF_MULTICAST) == 0)
+ goto skip;
+ if (!ifcp) {
+ /* new interface */
+ ifcp = (struct ifc *)malloc(sizeof(*ifcp));
+ memset(ifcp, 0, sizeof(*ifcp));
+ ifcp->ifc_index = -1;
+ ifcp->ifc_next = ifc;
+ ifc = ifcp;
+ nifc++;
+ ifcp->ifc_name = allocopy(ifrp->ifr_name);
+ ifcp->ifc_addr = 0;
+ ifcp->ifc_filter = 0;
+ ifcp->ifc_flags = ifr.ifr_flags;
+ trace(1, "newif %s <%s>\n", ifcp->ifc_name,
+ ifflags(ifcp->ifc_flags));
+ if (!strcmp(ifcp->ifc_name, LOOPBACK_IF))
+ loopifcp = ifcp;
+ } else {
+ /* update flag, this may be up again */
+ if (ifcp->ifc_flags != ifr.ifr_flags) {
+ trace(1, "%s: <%s> -> ", ifcp->ifc_name,
+ ifflags(ifcp->ifc_flags));
+ trace(1, "<%s>\n", ifflags(ifr.ifr_flags));
+ }
+ ifcp->ifc_flags = ifr.ifr_flags;
+ }
+ ifconfig1(ifrp, ifcp, s);
+ if ((ifcp->ifc_flags & (IFF_LOOPBACK | IFF_UP)) == IFF_UP
+ && 0 < ifcp->ifc_index && !ifcp->ifc_joined) {
+ mreq.ipv6mr_multiaddr = ifcp->ifc_ripsin.sin6_addr;
+ mreq.ipv6mr_interface = ifcp->ifc_index;
+ if (setsockopt(ripsock, IPPROTO_IPV6,
+ IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) < 0)
+ fatal("IPV6_JOIN_GROUP");
+ trace(1, "join %s %s\n", ifcp->ifc_name, RIP6_DEST);
+ ifcp->ifc_joined++;
+ }
+skip:
+ i += IFNAMSIZ;
+ if (ifrp->ifr_addr.sa_len > sizeof(struct sockaddr))
+ i += ifrp->ifr_addr.sa_len;
+ else
+ i += sizeof(struct sockaddr);
+ }
+ close(s);
+ free(buf);
+}
+
+void
+ifconfig1(ifrp, ifcp, s)
+ struct ifreq *ifrp;
+ struct ifc *ifcp;
+ int s;
+{
+ struct in6_ifreq ifr;
+ struct sockaddr_in6 *sin;
+ struct ifac *ifa;
+ int plen;
+ char buf[BUFSIZ];
+
+ sin = (struct sockaddr_in6 *)&ifrp->ifr_addr;
+ ifr.ifr_addr = *sin;
+ strcpy(ifr.ifr_name, ifrp->ifr_name);
+ if (ioctl(s, SIOCGIFNETMASK_IN6, (char *)&ifr) < 0)
+ fatal("ioctl: SIOCGIFNETMASK_IN6");
+ plen = mask2len(&ifr.ifr_addr.sin6_addr, 16);
+ if ((ifa = ifa_match(ifcp, &sin->sin6_addr, plen)) != NULL) {
+ /* same interface found */
+ /* need check if something changed */
+ /* XXX not yet implemented */
+ return;
+ }
+ /*
+ * New address is found
+ */
+ if ((ifa = MALLOC(struct ifac)) == NULL)
+ fatal("malloc: struct ifac");
+ ifa->ifa_conf = ifcp;
+ ifa->ifa_next = ifcp->ifc_addr;
+ ifcp->ifc_addr = ifa;
+ ifa->ifa_addr = sin->sin6_addr;
+ ifa->ifa_plen = plen;
+ if (ifcp->ifc_flags & IFF_POINTOPOINT) {
+ ifr.ifr_addr = *sin;
+ if (ioctl(s, SIOCGIFDSTADDR_IN6, (char *)&ifr) < 0)
+ fatal("ioctl: SIOCGIFDSTADDR_IN6");
+ ifa->ifa_raddr = ifr.ifr_dstaddr.sin6_addr;
+ inet_ntop(AF_INET6, (void *)&ifa->ifa_raddr, buf, sizeof(buf));
+ trace(1, "found address %s/%d -- %s\n",
+ inet6_n2p(&ifa->ifa_addr), ifa->ifa_plen, buf);
+ } else {
+ trace(1, "found address %s/%d\n",
+ inet6_n2p(&ifa->ifa_addr), ifa->ifa_plen);
+ }
+ if (ifcp->ifc_index < 0 && IN6_IS_ADDR_LINKLOCAL(&ifa->ifa_addr)) {
+ ifcp->ifc_mylladdr = ifa->ifa_addr;
+ ifcp->ifc_index = IN6_LINKLOCAL_IFINDEX(ifa->ifa_addr);
+ memcpy(&ifcp->ifc_ripsin, &ripsin, ripsin.ss_len);
+ SET_IN6_LINKLOCAL_IFINDEX(ifcp->ifc_ripsin.sin6_addr,
+ ifcp->ifc_index);
+ setindex2ifc(ifcp->ifc_index, ifcp);
+ ifcp->ifc_mtu = getifmtu(ifcp->ifc_index);
+ if (ifcp->ifc_mtu > RIP6_MAXMTU)
+ ifcp->ifc_mtu = RIP6_MAXMTU;
+ if (ioctl(s, SIOCGIFMETRIC, (char *)&ifr) < 0)
+ fatal("ioctl: SIOCGIFMETRIC");
+ ifcp->ifc_metric = ifr.ifr_metric;
+ trace(1, "\tindex: %d, mtu: %d, metric: %d\n",
+ ifcp->ifc_index, ifcp->ifc_mtu, ifcp->ifc_metric);
+ }
+}
+
+/*
+ * Receive and process routing messages.
+ * Update interface information as necesssary.
+ */
+void
+rtrecv()
+{
+ char buf[BUFSIZ];
+ char *p, *q;
+ struct rt_msghdr *rtm;
+ struct ifa_msghdr *ifam;
+ struct if_msghdr *ifm;
+ int len;
+ struct ifc *ifcp;
+ int iface = 0, rtable = 0;
+ struct sockaddr_in6 *rta[RTAX_MAX];
+ int i, addrs;
+
+ if ((len = read(rtsock, buf, sizeof(buf))) < 0) {
+ perror("read from rtsock");
+ exit(-1);
+ }
+ if (len < sizeof(*rtm)) {
+ trace(1, "short read from rtsock: %d (should be > %d)\n",
+ len, sizeof(*rtm));
+ return;
+ }
+
+ for (p = buf; p - buf < len; p += ((struct rt_msghdr *)p)->rtm_msglen) {
+ /* safety against bogus message */
+ if (((struct rt_msghdr *)p)->rtm_msglen <= 0) {
+ trace(1, "bogus rtmsg: length=%d\n",
+ ((struct rt_msghdr *)p)->rtm_msglen);
+ break;
+ }
+ rtm = NULL;
+ ifam = NULL;
+ ifm = NULL;
+ switch (((struct rt_msghdr *)p)->rtm_type) {
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ ifam = (struct ifa_msghdr *)p;
+ addrs = ifam->ifam_addrs;
+ q = (char *)(ifam + 1);
+ break;
+ case RTM_IFINFO:
+ ifm = (struct if_msghdr *)p;
+ addrs = ifm->ifm_addrs;
+ q = (char *)(ifm + 1);
+ break;
+ default:
+ rtm = (struct rt_msghdr *)p;
+ addrs = rtm->rtm_addrs;
+ q = (char *)(rtm + 1);
+ if (rtm->rtm_version != RTM_VERSION) {
+ trace(1, "unexpected rtmsg version %d "
+ "(should be %d)\n",
+ rtm->rtm_version, RTM_VERSION);
+ continue;
+ }
+ if (rtm->rtm_pid == pid) {
+#if 0
+ trace(1, "rtmsg looped back to me, ignored\n");
+#endif
+ continue;
+ }
+ break;
+ }
+ memset(&rta, 0, sizeof(rta));
+ for (i = 0; i < RTAX_MAX; i++) {
+ if (addrs & (1 << i)) {
+ rta[i] = (struct sockaddr_in6 *)q;
+ q += ROUNDUP(rta[i]->sin6_len);
+ }
+ }
+
+ trace(1, "rtsock: %s (addrs=%x)\n",
+ rttypes((struct rt_msghdr *)p), addrs);
+ if (dflag >= 2) {
+ int i;
+ for (i = 0;
+ i < ((struct rt_msghdr *)p)->rtm_msglen;
+ i++) {
+ fprintf(stderr, "%02x ", p[i] & 0xff);
+ if (i % 16 == 15) fprintf(stderr, "\n");
+ }
+ fprintf(stderr, "\n");
+ }
+
+ /*
+ * Easy ones first.
+ *
+ * We may be able to optimize by using ifm->ifm_index or
+ * ifam->ifam_index. For simplicity we don't do that here.
+ */
+ switch (((struct rt_msghdr *)p)->rtm_type) {
+ case RTM_NEWADDR:
+ case RTM_IFINFO:
+ iface++;
+ continue;
+ case RTM_ADD:
+ rtable++;
+ continue;
+ case RTM_LOSING:
+ case RTM_MISS:
+ case RTM_RESOLVE:
+ case RTM_GET:
+ case RTM_LOCK:
+ /* nothing to be done here */
+ trace(1, "\tnothing to be done, ignored\n");
+ continue;
+ }
+
+#if 0
+ if (rta[RTAX_DST] == NULL) {
+ trace(1, "\tno destination, ignored\n");
+ continue;
+ }
+ if (rta[RTAX_DST]->sin6_family != AF_INET6) {
+ trace(1, "\taf mismatch, ignored\n");
+ continue;
+ }
+ if (IN6_IS_ADDR_LINKLOCAL(&rta[RTAX_DST]->sin6_addr)) {
+ trace(1, "\tlinklocal destination, ignored\n");
+ continue;
+ }
+ if (IN6_ARE_ADDR_EQUAL(&rta[RTAX_DST]->sin6_addr, &in6addr_loopback)) {
+ trace(1, "\tloopback destination, ignored\n");
+ continue; /* Loopback */
+ }
+ if (IN6_IS_ADDR_MULTICAST(&rta[RTAX_DST]->sin6_addr)) {
+ trace(1, "\tmulticast destination, ignored\n");
+ continue;
+ }
+#endif
+
+ /* hard ones */
+ switch (((struct rt_msghdr *)p)->rtm_type) {
+ case RTM_NEWADDR:
+ case RTM_IFINFO:
+ case RTM_ADD:
+ case RTM_LOSING:
+ case RTM_MISS:
+ case RTM_RESOLVE:
+ case RTM_GET:
+ case RTM_LOCK:
+ /* should already be handled */
+ fatal("rtrecv: never reach here");
+ case RTM_DELETE:
+ if (!rta[RTAX_DST] || !rta[RTAX_GATEWAY]
+ || !rta[RTAX_NETMASK]) {
+ trace(1, "\tsome of dst/gw/netamsk are unavailable, ignored\n");
+ break;
+ }
+ if (rt_del(rta[RTAX_DST], rta[RTAX_GATEWAY], rta[RTAX_NETMASK]) == 0) {
+ rtable++; /*just to be sure*/
+ }
+ break;
+ case RTM_CHANGE:
+ case RTM_REDIRECT:
+ trace(1, "\tnot supported yet, ignored\n");
+ break;
+ case RTM_DELADDR:
+ if (!rta[RTAX_NETMASK] || !rta[RTAX_IFA]) {
+ trace(1, "\tno netmask or ifa given, ignored\n");
+ break;
+ }
+ if (ifam->ifam_index < nindex2ifc)
+ ifcp = index2ifc[ifam->ifam_index];
+ else
+ ifcp = NULL;
+ if (!ifcp) {
+ trace(1, "\tinvalid ifam_index %d, ignored\n",
+ ifam->ifam_index);
+ break;
+ }
+ rt_deladdr(ifcp, rta[RTAX_IFA], rta[RTAX_NETMASK]);
+ iface++;
+ break;
+ case RTM_OLDADD:
+ case RTM_OLDDEL:
+ trace(1, "\tnot supported yet, ignored\n");
+ break;
+ }
+
+ }
+
+ if (iface) {
+ trace(1, "rtsock: reconfigure interfaces, refresh interface routes\n");
+ ifconfig();
+ for (ifcp = ifc; ifcp; ifcp = ifcp->ifc_next)
+ ifrt(ifcp, 1);
+ }
+ if (rtable) {
+ trace(1, "rtsock: read routing table again\n");
+ krtread(1);
+ }
+}
+
+/*
+ * remove specified route from the internal routing table.
+ */
+int
+rt_del(sdst, sgw, smask)
+ const struct sockaddr_in6 *sdst;
+ const struct sockaddr_in6 *sgw;
+ const struct sockaddr_in6 *smask;
+{
+ const struct in6_addr *dst = NULL;
+ const struct in6_addr *gw = NULL;
+ int prefix;
+ struct netinfo6 ni6;
+ struct riprt *rrt = NULL;
+ time_t t_lifetime;
+
+ if (sdst->sin6_family != AF_INET6) {
+ trace(1, "\tother AF, ignored\n");
+ return -1;
+ }
+ if (IN6_IS_ADDR_LINKLOCAL(&sdst->sin6_addr)
+ || IN6_ARE_ADDR_EQUAL(&sdst->sin6_addr, &in6addr_loopback)
+ || IN6_IS_ADDR_MULTICAST(&sdst->sin6_addr)) {
+ trace(1, "\taddress %s not interesting, ignored\n",
+ inet6_n2p(&sdst->sin6_addr));
+ return -1;
+ }
+ dst = &sdst->sin6_addr;
+ if (sgw->sin6_family == AF_INET6
+ && smask->sin6_family == AF_INET6) {
+ /* easy case */
+ gw = &sgw->sin6_addr;
+ prefix = mask2len(&smask->sin6_addr, 16);
+ } else if (sgw->sin6_family == AF_LINK) {
+ /*
+ * Interface route... a hard case. We need to get the prefix
+ * length from the kernel, but we now are parsing rtmsg.
+ * We'll purge matching routes from my list, then get the
+ * fresh list.
+ */
+ struct riprt *longest;
+ trace(1, "\t%s is a interface route, guessing prefixlen\n",
+ inet6_n2p(dst));
+ longest = NULL;
+ for (rrt = riprt; rrt; rrt = rrt->rrt_next) {
+ if (IN6_ARE_ADDR_EQUAL(&rrt->rrt_info.rip6_dest,
+ &sdst->sin6_addr)
+ && IN6_IS_ADDR_LOOPBACK(&rrt->rrt_gw)) {
+ if (!longest
+ || longest->rrt_info.rip6_plen <
+ rrt->rrt_info.rip6_plen) {
+ longest = rrt;
+ }
+ }
+ }
+ rrt = longest;
+ if (!rrt) {
+ trace(1, "\tno matching interface route found\n");
+ return -1;
+ }
+ gw = &in6addr_loopback;
+ prefix = rrt->rrt_info.rip6_plen;
+ } else {
+ trace(1, "\tunsupported af: (gw=%d, mask=%d)\n",
+ sgw->sin6_family, smask->sin6_family);
+ return -1;
+ }
+
+ trace(1, "\tdeleting %s/%d ", inet6_n2p(dst), prefix);
+ trace(1, "gw %s\n", inet6_n2p(gw));
+ t_lifetime = time(NULL) - RIP_LIFETIME;
+ /* age route for interface address */
+ memset(&ni6, 0, sizeof(ni6));
+ ni6.rip6_dest = *dst;
+ ni6.rip6_plen = prefix;
+ applyplen(&ni6.rip6_dest, ni6.rip6_plen); /*to be sure*/
+ trace(1, "\tfind route %s/%d\n", inet6_n2p(&ni6.rip6_dest),
+ ni6.rip6_plen);
+ if (!rrt && (rrt = rtsearch(&ni6)) == NULL) {
+ trace(1, "\tno route found\n");
+ return -1;
+ }
+ if ((rrt->rrt_flags & RTF_STATIC) == 0) {
+ trace(1, "\tyou can delete static routes only\n");
+ } else if (memcmp(&rrt->rrt_gw, gw, sizeof(rrt->rrt_gw)) != 0) {
+ trace(1, "\tgw mismatch: %s <-> ",
+ inet6_n2p(&rrt->rrt_gw));
+ trace(1, "%s\n", inet6_n2p(gw));
+ } else {
+ trace(1, "\troute found, age it\n");
+ if (rrt->rrt_t == 0 || rrt->rrt_t > t_lifetime) {
+ rrt->rrt_t = t_lifetime;
+ rrt->rrt_info.rip6_metric = HOPCNT_INFINITY6;
+ }
+ }
+ return 0;
+}
+
+/*
+ * remove specified address from internal interface/routing table.
+ */
+int
+rt_deladdr(ifcp, sifa, smask)
+ struct ifc *ifcp;
+ const struct sockaddr_in6 *sifa;
+ const struct sockaddr_in6 *smask;
+{
+ const struct in6_addr *addr = NULL;
+ int prefix;
+ struct ifac *ifa = NULL;
+ struct netinfo6 ni6;
+ struct riprt *rrt = NULL;
+ time_t t_lifetime;
+ int updated = 0;
+
+ if (sifa->sin6_family != AF_INET6 || smask->sin6_family != AF_INET6) {
+ trace(1, "\tother AF, ignored\n");
+ return -1;
+ }
+ addr = &sifa->sin6_addr;
+ prefix = mask2len(&smask->sin6_addr, 16);
+
+ trace(1, "\tdeleting %s/%d from %s\n",
+ inet6_n2p(addr), prefix, ifcp->ifc_name);
+ ifa = ifa_match(ifcp, addr, prefix);
+ if (!ifa) {
+ trace(1, "\tno matching ifa found for %s/%d on %s\n",
+ inet6_n2p(addr), prefix, ifcp->ifc_name);
+ return -1;
+ }
+ if (ifa->ifa_conf != ifcp) {
+ trace(1, "\taddress table corrupt: back pointer does not match "
+ "(%s != %s)\n",
+ ifcp->ifc_name, ifa->ifa_conf->ifc_name);
+ return -1;
+ }
+ /* remove ifa from interface */
+ if (ifcp->ifc_addr == ifa)
+ ifcp->ifc_addr = ifa->ifa_next;
+ else {
+ struct ifac *p;
+ for (p = ifcp->ifc_addr; p; p = p->ifa_next) {
+ if (p->ifa_next == ifa) {
+ p->ifa_next = ifa->ifa_next;
+ break;
+ }
+ }
+ }
+ ifa->ifa_next = NULL;
+ ifa->ifa_conf = NULL;
+ t_lifetime = time(NULL) - RIP_LIFETIME;
+ /* age route for interface address */
+ memset(&ni6, 0, sizeof(ni6));
+ ni6.rip6_dest = ifa->ifa_addr;
+ ni6.rip6_plen = ifa->ifa_plen;
+ applyplen(&ni6.rip6_dest, ni6.rip6_plen);
+ trace(1, "\tfind interface route %s/%d on %d\n",
+ inet6_n2p(&ni6.rip6_dest), ni6.rip6_plen, ifcp->ifc_index);
+ if ((rrt = rtsearch(&ni6)) != NULL) {
+ struct in6_addr none;
+ memset(&none, 0, sizeof(none));
+ if (rrt->rrt_index == ifcp->ifc_index
+ && memcmp(&rrt->rrt_gw, &none, sizeof(rrt->rrt_gw)) == 0) {
+ trace(1, "\troute found, age it\n");
+ if (rrt->rrt_t == 0 || rrt->rrt_t > t_lifetime) {
+ rrt->rrt_t = t_lifetime;
+ rrt->rrt_info.rip6_metric = HOPCNT_INFINITY6;
+ }
+ updated++;
+ } else {
+ trace(1, "\tnon-interface route found: %s/%d on %d\n",
+ inet6_n2p(&rrt->rrt_info.rip6_dest),
+ rrt->rrt_info.rip6_plen,
+ rrt->rrt_index);
+ }
+ } else
+ trace(1, "\tno interface route found\n");
+ /* age route for p2p destination */
+ if (ifcp->ifc_flags & IFF_POINTOPOINT) {
+ memset(&ni6, 0, sizeof(ni6));
+ ni6.rip6_dest = ifa->ifa_raddr;
+ ni6.rip6_plen = 128;
+ applyplen(&ni6.rip6_dest, ni6.rip6_plen); /*to be sure*/
+ trace(1, "\tfind p2p route %s/%d on %d\n",
+ inet6_n2p(&ni6.rip6_dest), ni6.rip6_plen,
+ ifcp->ifc_index);
+ if ((rrt = rtsearch(&ni6)) != NULL) {
+ if (rrt->rrt_index == ifcp->ifc_index
+ && memcmp(&rrt->rrt_gw, &ifa->ifa_addr,
+ sizeof(rrt->rrt_gw)) == 0) {
+ trace(1, "\troute found, age it\n");
+ if (rrt->rrt_t == 0 || rrt->rrt_t > t_lifetime) {
+ rrt->rrt_t = t_lifetime;
+ rrt->rrt_info.rip6_metric =
+ HOPCNT_INFINITY6;
+ updated++;
+ }
+ } else {
+ trace(1, "\tnon-p2p route found: %s/%d on %d\n",
+ inet6_n2p(&rrt->rrt_info.rip6_dest),
+ rrt->rrt_info.rip6_plen,
+ rrt->rrt_index);
+ }
+ } else
+ trace(1, "\tno p2p route found\n");
+ }
+ return updated ? 0 : -1;
+}
+
+/*
+ * Get each interface address and put those interface routes to the route
+ * list.
+ */
+void
+ifrt(ifcp, again)
+ struct ifc *ifcp;
+ int again;
+{
+ struct ifac *ifa;
+ struct riprt *rrt;
+ struct netinfo6 *np;
+
+ if (ifcp->ifc_flags & IFF_LOOPBACK)
+ return; /* ignore loopback */
+ for (ifa = ifcp->ifc_addr; ifa; ifa = ifa->ifa_next) {
+ if (IN6_IS_ADDR_LINKLOCAL(&ifa->ifa_addr))
+ continue; /* don't advertise link local addr */
+ if ((rrt = MALLOC(struct riprt)) == NULL)
+ fatal("malloc: struct riprt");
+ rrt->rrt_same = NULL;
+ rrt->rrt_index = ifcp->ifc_index;
+ rrt->rrt_t = 0; /* don't age */
+ rrt->rrt_info.rip6_dest = ifa->ifa_addr;
+ rrt->rrt_info.rip6_tag = htons(routetag & 0xffff);
+ rrt->rrt_info.rip6_metric = 1 + ifcp->ifc_metric;
+ rrt->rrt_info.rip6_plen = ifa->ifa_plen;
+ applyplen(&rrt->rrt_info.rip6_dest, ifa->ifa_plen);
+ memset(&rrt->rrt_gw, 0, sizeof(struct in6_addr));
+ np = &rrt->rrt_info;
+ if (rtsearch(np) == NULL) {
+ /* Attach the route to the list */
+ rrt->rrt_next = riprt;
+ riprt = rrt;
+ } else {
+ /* Already found */
+ if (!again) {
+ trace(1, "route: %s/%d: already registered\n",
+ inet6_n2p(&np->rip6_dest),
+ np->rip6_plen);
+ }
+ free(rrt);
+ }
+
+ if (ifcp->ifc_flags & IFF_POINTOPOINT) {
+ if ((rrt = MALLOC(struct riprt)) == NULL)
+ fatal("malloc: struct riprt");
+ rrt->rrt_same = NULL;
+ rrt->rrt_index = ifcp->ifc_index;
+ rrt->rrt_t = 0; /* Don't age */
+ rrt->rrt_info.rip6_dest = ifa->ifa_raddr;
+ rrt->rrt_info.rip6_tag = htons(routetag & 0xffff);
+ rrt->rrt_info.rip6_metric = 1;
+ rrt->rrt_info.rip6_plen = 128;
+ rrt->rrt_gw = ifa->ifa_addr;
+ rrt->rrt_flags |= RTF_NOADVERTISE;
+ np = &rrt->rrt_info;
+ if (rtsearch(np) == NULL) {
+ /* Attach the route to the list */
+ rrt->rrt_next = riprt;
+ riprt = rrt;
+ } else {
+ /* Already found */
+ if (!again) {
+ trace(1, "route: %s/%d: already registered\n",
+ inet6_n2p(&np->rip6_dest),
+ np->rip6_plen);
+ }
+ free(rrt);
+ }
+ }
+ }
+}
+
+int
+getifmtu(ifindex)
+ int ifindex;
+{
+ int mib[6];
+ char *buf;
+ size_t msize;
+ struct if_msghdr *ifm;
+ int mtu;
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = AF_INET6;
+ mib[4] = NET_RT_IFLIST;
+ mib[5] = ifindex;
+ if (sysctl(mib, 6, NULL, &msize, NULL, 0) < 0)
+ fatal("sysctl estimate NET_RT_IFLIST");
+ if ((buf = malloc(msize)) == NULL)
+ fatal("malloc");
+ if (sysctl(mib, 6, buf, &msize, NULL, 0) < 0)
+ fatal("sysctl NET_RT_IFLIST");
+ ifm = (struct if_msghdr *)buf;
+ mtu = ifm->ifm_data.ifi_mtu;
+#ifdef __FREEBSD__
+ if (ifindex != ifm->ifm_index)
+ fatal("ifindex does not match with ifm_index");
+#endif /* __FREEBSD__ */
+ free(buf);
+ return mtu;
+}
+
+const char *
+rttypes(rtm)
+ struct rt_msghdr *rtm;
+{
+#define RTTYPE(s, f) if (rtm->rtm_type == (f)) return (s)
+ RTTYPE("ADD", RTM_ADD);
+ RTTYPE("DELETE", RTM_DELETE);
+ RTTYPE("CHANGE", RTM_CHANGE);
+ RTTYPE("GET", RTM_GET);
+ RTTYPE("LOSING", RTM_LOSING);
+ RTTYPE("REDIRECT", RTM_REDIRECT);
+ RTTYPE("MISS", RTM_MISS);
+ RTTYPE("LOCK", RTM_LOCK);
+ RTTYPE("OLDADD", RTM_OLDADD);
+ RTTYPE("OLDDEL", RTM_OLDDEL);
+ RTTYPE("RESOLVE", RTM_RESOLVE);
+ RTTYPE("NEWADDR", RTM_NEWADDR);
+ RTTYPE("DELADDR", RTM_DELADDR);
+ RTTYPE("IFINFO", RTM_IFINFO);
+#undef RTTYPE
+ return NULL;
+}
+
+const char *
+rtflags(rtm)
+ struct rt_msghdr *rtm;
+{
+ static char buf[BUFSIZ];
+
+ strcpy(buf, "");
+#define RTFLAG(s, f) if (rtm->rtm_flags & (f)) strcat(buf, (s))
+ RTFLAG("U", RTF_UP);
+ RTFLAG("G", RTF_GATEWAY);
+ RTFLAG("H", RTF_HOST);
+ RTFLAG("R", RTF_REJECT);
+ RTFLAG("D", RTF_DYNAMIC);
+ RTFLAG("M", RTF_MODIFIED);
+ RTFLAG("d", RTF_DONE);
+#ifdef RTF_MASK
+ RTFLAG("m", RTF_MASK);
+#endif
+ RTFLAG("C", RTF_CLONING);
+ RTFLAG("X", RTF_XRESOLVE);
+ RTFLAG("L", RTF_LLINFO);
+ RTFLAG("S", RTF_STATIC);
+ RTFLAG("B", RTF_BLACKHOLE);
+ RTFLAG("2", RTF_PROTO2);
+ RTFLAG("1", RTF_PROTO1);
+#undef RTFLAG
+ return buf;
+}
+
+const char *
+ifflags(flags)
+ int flags;
+{
+ static char buf[BUFSIZ];
+
+ strcpy(buf, "");
+#define IFFLAG(s, f) \
+ if (flags & f) { if (buf[0]) strcat(buf, ","); strcat(buf, s); }
+ IFFLAG("UP", IFF_UP);
+ IFFLAG("BROADCAST", IFF_BROADCAST);
+ IFFLAG("DEBUG", IFF_DEBUG);
+ IFFLAG("LOOPBACK", IFF_LOOPBACK);
+ IFFLAG("POINTOPOINT", IFF_POINTOPOINT);
+#ifdef IFF_NOTRAILERS
+ IFFLAG("NOTRAILERS", IFF_NOTRAILERS);
+#endif
+ IFFLAG("RUNNING", IFF_RUNNING);
+ IFFLAG("NOARP", IFF_NOARP);
+ IFFLAG("PROMISC", IFF_PROMISC);
+ IFFLAG("ALLMULTI", IFF_ALLMULTI);
+ IFFLAG("OACTIVE", IFF_OACTIVE);
+ IFFLAG("SIMPLEX", IFF_SIMPLEX);
+ IFFLAG("LINK0", IFF_LINK0);
+ IFFLAG("LINK1", IFF_LINK1);
+ IFFLAG("LINK2", IFF_LINK2);
+ IFFLAG("MULTICAST", IFF_MULTICAST);
+#undef IFFLAG
+ return buf;
+}
+
+void
+krtread(again)
+ int again;
+{
+ int mib[6];
+ size_t msize;
+ char *buf, *p, *lim;
+ struct rt_msghdr *rtm;
+ int retry;
+ const char *errmsg;
+
+ retry = 0;
+ buf = NULL;
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = AF_INET6; /* Address family */
+ mib[4] = NET_RT_DUMP; /* Dump the kernel routing table */
+ mib[5] = 0; /* No flags */
+ do {
+ retry++;
+ errmsg = NULL;
+ if (buf)
+ free(buf);
+ if (sysctl(mib, 6, NULL, &msize, NULL, 0) < 0) {
+ errmsg = "sysctl estimate";
+ continue;
+ }
+ if ((buf = malloc(msize)) == NULL) {
+ errmsg = "malloc";
+ continue;
+ }
+ if (sysctl(mib, 6, buf, &msize, NULL, 0) < 0) {
+ errmsg = "sysctl NET_RT_DUMP";
+ continue;
+ }
+ } while (retry < 5 && errmsg != NULL);
+ if (errmsg)
+ fatal("%s (with %d retries, msize=%d)", errmsg, retry, msize);
+ else if (1 < retry)
+ syslog(LOG_INFO, "NET_RT_DUMP %d retires", retry);
+
+ lim = buf + msize;
+ for (p = buf; p < lim; p += rtm->rtm_msglen) {
+ rtm = (struct rt_msghdr *)p;
+ rt_entry(rtm, again);
+ }
+ free(buf);
+}
+
+void
+rt_entry(rtm, again)
+ struct rt_msghdr *rtm;
+ int again;
+{
+ struct sockaddr_in6 *sin6_dst, *sin6_gw, *sin6_mask;
+ struct sockaddr_in6 *sin6_genmask, *sin6_ifp;
+ char *rtmp, *ifname = NULL;
+ char buf[BUFSIZ];
+ struct riprt *rrt;
+ struct netinfo6 *np;
+ int s;
+
+ sin6_dst = sin6_gw = sin6_mask = sin6_genmask = sin6_ifp = 0;
+ if ((rtm->rtm_flags & RTF_UP) == 0 || rtm->rtm_flags &
+ (RTF_CLONING|RTF_XRESOLVE|RTF_LLINFO|RTF_BLACKHOLE))
+ return; /* not interested in the link route */
+ rtmp = (char *)(rtm + 1);
+ /* Destination */
+ if ((rtm->rtm_addrs & RTA_DST) == 0)
+ return; /* ignore routes without destination address */
+ sin6_dst = (struct sockaddr_in6 *)rtmp;
+ rtmp += sin6_dst->sin6_len;
+ if (rtm->rtm_addrs & RTA_GATEWAY) {
+ sin6_gw = (struct sockaddr_in6 *)rtmp;
+ rtmp += ROUNDUP(sin6_gw->sin6_len);
+ }
+ if (rtm->rtm_addrs & RTA_NETMASK) {
+ sin6_mask = (struct sockaddr_in6 *)rtmp;
+ rtmp += ROUNDUP(sin6_mask->sin6_len);
+ }
+ if (rtm->rtm_addrs & RTA_GENMASK) {
+ sin6_genmask = (struct sockaddr_in6 *)rtmp;
+ rtmp += ROUNDUP(sin6_genmask->sin6_len);
+ }
+ if (rtm->rtm_addrs & RTA_IFP) {
+ sin6_ifp = (struct sockaddr_in6 *)rtmp;
+ rtmp += ROUNDUP(sin6_ifp->sin6_len);
+ }
+
+ /* Destination */
+ if (sin6_dst->sin6_family != AF_INET6)
+ return;
+ if (IN6_IS_ADDR_LINKLOCAL(&sin6_dst->sin6_addr))
+ return; /* Link-local */
+ if (IN6_ARE_ADDR_EQUAL(&sin6_dst->sin6_addr, &in6addr_loopback))
+ return; /* Loopback */
+ if (IN6_IS_ADDR_MULTICAST(&sin6_dst->sin6_addr))
+ return;
+
+ if ((rrt = MALLOC(struct riprt)) == NULL)
+ fatal("malloc: struct riprt");
+ np = &rrt->rrt_info;
+ rrt->rrt_same = NULL;
+ rrt->rrt_t = time(NULL);
+ if (aflag == 0 && (rtm->rtm_flags & RTF_STATIC))
+ rrt->rrt_t = 0; /* Don't age static routes */
+#if 0
+ np->rip6_tag = htons(routetag & 0xffff);
+#else
+ np->rip6_tag = 0;
+#endif
+ np->rip6_metric = rtm->rtm_rmx.rmx_hopcount;
+ if (np->rip6_metric < 1)
+ np->rip6_metric = 1;
+ rrt->rrt_flags = rtm->rtm_flags;
+ np->rip6_dest = sin6_dst->sin6_addr;
+
+ /* Mask or plen */
+ if (rtm->rtm_flags & RTF_HOST)
+ np->rip6_plen = 128; /* Host route */
+ else if (sin6_mask) {
+ np->rip6_plen = mask2len(&sin6_mask->sin6_addr,
+ sin6_mask->sin6_len - offsetof(struct sockaddr_in6, sin6_addr));
+ } else
+ np->rip6_plen = 0;
+
+ if (rtsearch(np)) {
+ /* Already found */
+ if (!again) {
+ trace(1, "route: %s/%d flags %s: already registered\n",
+ inet6_n2p(&np->rip6_dest), np->rip6_plen,
+ rtflags(rtm));
+ }
+ free(rrt);
+ return;
+ }
+ /* Gateway */
+ if (!sin6_gw)
+ memset(&rrt->rrt_gw, 0, sizeof(struct in6_addr));
+ else {
+ if (sin6_gw->sin6_family == AF_INET6)
+ rrt->rrt_gw = sin6_gw->sin6_addr;
+ else if (sin6_gw->sin6_family == AF_LINK) {
+ /* XXX in case ppp link? */
+ rrt->rrt_gw = in6addr_loopback;
+ } else
+ memset(&rrt->rrt_gw, 0, sizeof(struct in6_addr));
+ }
+ trace(1, "route: %s/%d flags %s",
+ inet6_n2p(&np->rip6_dest), np->rip6_plen, rtflags(rtm));
+ trace(1, " gw %s", inet6_n2p(&rrt->rrt_gw));
+
+ /* Interface */
+ s = rtm->rtm_index;
+ if (s < nindex2ifc && index2ifc[s])
+ ifname = index2ifc[s]->ifc_name;
+ else
+ fatal("Unknown interface %d", s);
+ trace(1, " if %s sock %d\n", ifname, s);
+ rrt->rrt_index = s;
+
+ /* Check gateway */
+ if (!IN6_IS_ADDR_LINKLOCAL(&rrt->rrt_gw) &&
+ !IN6_IS_ADDR_LOOPBACK(&rrt->rrt_gw)
+#ifdef __FreeBSD__
+ && (rrt->rrt_flags & RTF_LOCAL) == 0
+#endif
+ ) {
+ inet_ntop(AF_INET6, (void *)&rrt->rrt_info.rip6_dest,
+ buf, sizeof(buf));
+ trace(0, "***** Gateway %s is not a link-local address.\n",
+ inet6_n2p(&rrt->rrt_gw));
+ trace(0, "***** dest(%s) if(%s) -- Not optimized.\n",
+ buf, ifname);
+ rrt->rrt_flags |= RTF_NH_NOT_LLADDR;
+ }
+
+ /* Put it to the route list */
+ rrt->rrt_next = riprt;
+ riprt = rrt;
+}
+
+int
+addroute(rrt, gw, ifcp)
+ struct riprt *rrt;
+ const struct in6_addr *gw;
+ struct ifc *ifcp;
+{
+ struct netinfo6 *np;
+ u_char buf[BUFSIZ], buf1[BUFSIZ], buf2[BUFSIZ];
+ struct rt_msghdr *rtm;
+ struct sockaddr_in6 *sin;
+ int len;
+
+ np = &rrt->rrt_info;
+ inet_ntop(AF_INET6, (void *)gw, (char *)buf1, sizeof(buf1));
+ inet_ntop(AF_INET6, (void *)&ifcp->ifc_mylladdr, (char *)buf2, sizeof(buf2));
+ tracet(1, "ADD: %s/%d gw %s [%d] ifa %s\n",
+ inet6_n2p(&np->rip6_dest), np->rip6_plen, buf1,
+ np->rip6_metric - 1, buf2);
+ if (rtlog)
+ fprintf(rtlog, "%s: ADD: %s/%d gw %s [%d] ifa %s\n", hms(),
+ inet6_n2p(&np->rip6_dest), np->rip6_plen, buf1,
+ np->rip6_metric - 1, buf2);
+ if (nflag)
+ return 0;
+
+ memset(buf, 0, sizeof(buf));
+ rtm = (struct rt_msghdr *)buf;
+ rtm->rtm_type = RTM_ADD;
+ rtm->rtm_version = RTM_VERSION;
+ rtm->rtm_seq = ++seq;
+ rtm->rtm_pid = pid;
+ rtm->rtm_flags = rrt->rrt_flags & RTF_ROUTE_H;
+ rtm->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
+ rtm->rtm_rmx.rmx_hopcount = np->rip6_metric - 1;
+ rtm->rtm_inits = RTV_HOPCOUNT;
+ sin = (struct sockaddr_in6 *)&buf[sizeof(struct rt_msghdr)];
+ /* Destination */
+ sin->sin6_len = sizeof(struct sockaddr_in6);
+ sin->sin6_family = AF_INET6;
+ sin->sin6_addr = np->rip6_dest;
+ sin = (struct sockaddr_in6 *)((char *)sin + ROUNDUP(sin->sin6_len));
+ /* Gateway */
+ sin->sin6_len = sizeof(struct sockaddr_in6);
+ sin->sin6_family = AF_INET6;
+ sin->sin6_addr = *gw;
+ sin = (struct sockaddr_in6 *)((char *)sin + ROUNDUP(sin->sin6_len));
+ /* Netmask */
+ sin->sin6_len = sizeof(struct sockaddr_in6);
+ sin->sin6_family = AF_INET6;
+ sin->sin6_addr = *(plen2mask(np->rip6_plen));
+ sin = (struct sockaddr_in6 *)((char *)sin + ROUNDUP(sin->sin6_len));
+
+ len = (char *)sin - (char *)buf;
+ rtm->rtm_msglen = len;
+ if (write(rtsock, buf, len) > 0)
+ return 0;
+
+ if (errno == EEXIST) {
+ trace(0, "ADD: Route already exists %s/%d gw %s\n",
+ inet6_n2p(&np->rip6_dest), np->rip6_plen, buf1);
+ if (rtlog)
+ fprintf(rtlog, "ADD: Route already exists %s/%d gw %s\n",
+ inet6_n2p(&np->rip6_dest), np->rip6_plen, buf1);
+ } else {
+ trace(0, "Can not write to rtsock (addroute): %s\n",
+ strerror(errno));
+ if (rtlog)
+ fprintf(rtlog, "\tCan not write to rtsock: %s\n",
+ strerror(errno));
+ }
+ return -1;
+}
+
+int
+delroute(np, gw)
+ struct netinfo6 *np;
+ struct in6_addr *gw;
+{
+ u_char buf[BUFSIZ], buf2[BUFSIZ];
+ struct rt_msghdr *rtm;
+ struct sockaddr_in6 *sin;
+ int len;
+
+ inet_ntop(AF_INET6, (void *)gw, (char *)buf2, sizeof(buf2));
+ tracet(1, "DEL: %s/%d gw %s\n", inet6_n2p(&np->rip6_dest),
+ np->rip6_plen, buf2);
+ if (rtlog)
+ fprintf(rtlog, "%s: DEL: %s/%d gw %s\n",
+ hms(), inet6_n2p(&np->rip6_dest), np->rip6_plen, buf2);
+ if (nflag)
+ return 0;
+
+ memset(buf, 0, sizeof(buf));
+ rtm = (struct rt_msghdr *)buf;
+ rtm->rtm_type = RTM_DELETE;
+ rtm->rtm_version = RTM_VERSION;
+ rtm->rtm_seq = ++seq;
+ rtm->rtm_pid = pid;
+ rtm->rtm_flags = RTF_UP | RTF_GATEWAY;
+ rtm->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
+ sin = (struct sockaddr_in6 *)&buf[sizeof(struct rt_msghdr)];
+ /* Destination */
+ sin->sin6_len = sizeof(struct sockaddr_in6);
+ sin->sin6_family = AF_INET6;
+ sin->sin6_addr = np->rip6_dest;
+ sin = (struct sockaddr_in6 *)((char *)sin + ROUNDUP(sin->sin6_len));
+ /* Gateway */
+ sin->sin6_len = sizeof(struct sockaddr_in6);
+ sin->sin6_family = AF_INET6;
+ sin->sin6_addr = *gw;
+ sin = (struct sockaddr_in6 *)((char *)sin + ROUNDUP(sin->sin6_len));
+ /* Netmask */
+ sin->sin6_len = sizeof(struct sockaddr_in6);
+ sin->sin6_family = AF_INET6;
+ sin->sin6_addr = *(plen2mask(np->rip6_plen));
+ sin = (struct sockaddr_in6 *)((char *)sin + ROUNDUP(sin->sin6_len));
+
+ len = (char *)sin - (char *)buf;
+ rtm->rtm_msglen = len;
+ if (write(rtsock, buf, len) >= 0)
+ return 0;
+
+ if (errno == ESRCH) {
+ trace(0, "RTDEL: Route does not exist: %s/%d gw %s\n",
+ inet6_n2p(&np->rip6_dest), np->rip6_plen, buf2);
+ if (rtlog)
+ fprintf(rtlog, "RTDEL: Route does not exist: %s/%d gw %s\n",
+ inet6_n2p(&np->rip6_dest), np->rip6_plen, buf2);
+ } else {
+ trace(0, "Can not write to rtsock (delroute): %s\n",
+ strerror(errno));
+ if (rtlog)
+ fprintf(rtlog, "\tCan not write to rtsock: %s\n",
+ strerror(errno));
+ }
+ return -1;
+}
+
+struct in6_addr *
+getroute(np, gw)
+ struct netinfo6 *np;
+ struct in6_addr *gw;
+{
+ u_char buf[BUFSIZ];
+ u_long myseq;
+ int len;
+ struct rt_msghdr *rtm;
+ struct sockaddr_in6 *sin;
+
+ rtm = (struct rt_msghdr *)buf;
+ len = sizeof(struct rt_msghdr) + sizeof(struct sockaddr_in6);
+ memset(rtm, 0, len);
+ rtm->rtm_type = RTM_GET;
+ rtm->rtm_version = RTM_VERSION;
+ myseq = ++seq;
+ rtm->rtm_seq = myseq;
+ rtm->rtm_addrs = RTA_DST;
+ rtm->rtm_msglen = len;
+ sin = (struct sockaddr_in6 *)&buf[sizeof(struct rt_msghdr)];
+ sin->sin6_len = sizeof(struct sockaddr_in6);
+ sin->sin6_family = AF_INET6;
+ sin->sin6_addr = np->rip6_dest;
+ if (write(rtsock, buf, len) < 0) {
+ if (errno == ESRCH) /* No such route found */
+ return NULL;
+ perror("write to rtsock");
+ exit(-1);
+ }
+ do {
+ if ((len = read(rtsock, buf, sizeof(buf))) < 0) {
+ perror("read from rtsock");
+ exit(-1);
+ }
+ rtm = (struct rt_msghdr *)buf;
+ } while (rtm->rtm_seq != myseq || rtm->rtm_pid != pid);
+ sin = (struct sockaddr_in6 *)&buf[sizeof(struct rt_msghdr)];
+ if (rtm->rtm_addrs & RTA_DST) {
+ sin = (struct sockaddr_in6 *)
+ ((char *)sin + ROUNDUP(sin->sin6_len));
+ }
+ if (rtm->rtm_addrs & RTA_GATEWAY) {
+ *gw = sin->sin6_addr;
+ return gw;
+ }
+ return NULL;
+}
+
+const char *
+inet6_n2p(p)
+ const struct in6_addr *p;
+{
+ static char buf[BUFSIZ];
+
+ return inet_ntop(AF_INET6, (void *)p, buf, sizeof(buf));
+}
+
+void
+ifrtdump(sig)
+ int sig;
+{
+
+ ifdump(sig);
+ rtdump(sig);
+}
+
+void
+ifdump(sig)
+ int sig;
+{
+ struct ifc *ifcp;
+ FILE *dump;
+ int i;
+
+ if (sig == 0)
+ dump = stderr;
+ else
+ if ((dump = fopen(ROUTE6D_DUMP, "a")) == NULL)
+ dump = stderr;
+
+ fprintf(dump, "%s: Interface Table Dump\n", hms());
+ fprintf(dump, " Number of interfaces: %d\n", nifc);
+ for (i = 0; i < 2; i++) {
+ fprintf(dump, " %sadvertising interfaces:\n", i ? "non-" : "");
+ for (ifcp = ifc; ifcp; ifcp = ifcp->ifc_next) {
+ if (i == 0) {
+ if ((ifcp->ifc_flags & IFF_UP) == 0)
+ continue;
+ if (iff_find(ifcp, 'N') != NULL)
+ continue;
+ } else {
+ if (ifcp->ifc_flags & IFF_UP)
+ continue;
+ }
+ ifdump0(dump, ifcp);
+ }
+ }
+ fprintf(dump, "\n");
+ if (dump != stderr)
+ fclose(dump);
+}
+
+void
+ifdump0(dump, ifcp)
+ FILE *dump;
+ const struct ifc *ifcp;
+{
+ struct ifac *ifa;
+ struct iff *iffp;
+ char buf[BUFSIZ];
+ const char *ft;
+ int addr;
+
+ fprintf(dump, " %s: index(%d) flags(%s) addr(%s) mtu(%d) metric(%d)\n",
+ ifcp->ifc_name, ifcp->ifc_index, ifflags(ifcp->ifc_flags),
+ inet6_n2p(&ifcp->ifc_mylladdr),
+ ifcp->ifc_mtu, ifcp->ifc_metric);
+ for (ifa = ifcp->ifc_addr; ifa; ifa = ifa->ifa_next) {
+ if (ifcp->ifc_flags & IFF_POINTOPOINT) {
+ inet_ntop(AF_INET6, (void *)&ifa->ifa_raddr,
+ buf, sizeof(buf));
+ fprintf(dump, "\t%s/%d -- %s\n",
+ inet6_n2p(&ifa->ifa_addr),
+ ifa->ifa_plen, buf);
+ } else {
+ fprintf(dump, "\t%s/%d\n",
+ inet6_n2p(&ifa->ifa_addr),
+ ifa->ifa_plen);
+ }
+ }
+ if (ifcp->ifc_filter) {
+ fprintf(dump, "\tFilter:");
+ for (iffp = ifcp->ifc_filter; iffp; iffp = iffp->iff_next) {
+ addr = 0;
+ switch (iffp->iff_type) {
+ case 'A':
+ ft = "Aggregate"; addr++; break;
+ case 'N':
+ ft = "No-advertise"; break;
+ case 'O':
+ ft = "Advertise-only"; addr++; break;
+ case 'T':
+ ft = "Default-only"; break;
+ case 'L':
+ ft = "Listen-only"; addr++; break;
+ default:
+ snprintf(buf, sizeof(buf), "Unknown-%c", iffp->iff_type);
+ ft = buf;
+ addr++;
+ break;
+ }
+ fprintf(dump, " %s", ft);
+ if (addr) {
+ fprintf(dump, "(%s/%d)", inet6_n2p(&iffp->iff_addr),
+ iffp->iff_plen);
+ }
+ }
+ fprintf(dump, "\n");
+ }
+}
+
+void
+rtdump(sig)
+ int sig;
+{
+ struct riprt *rrt;
+ char buf[BUFSIZ];
+ FILE *dump;
+ time_t t, age;
+
+ if (sig == 0)
+ dump = stderr;
+ else
+ if ((dump = fopen(ROUTE6D_DUMP, "a")) == NULL)
+ dump = stderr;
+
+ t = time(NULL);
+ fprintf(dump, "\n%s: Routing Table Dump\n", hms());
+ for (rrt = riprt; rrt; rrt = rrt->rrt_next) {
+ if (rrt->rrt_t == 0)
+ age = 0;
+ else
+ age = t - rrt->rrt_t;
+ inet_ntop(AF_INET6, (void *)&rrt->rrt_info.rip6_dest,
+ buf, sizeof(buf));
+ fprintf(dump, " %s/%d if(%d:%s) gw(%s) [%d] age(%ld)",
+ buf, rrt->rrt_info.rip6_plen, rrt->rrt_index,
+ index2ifc[rrt->rrt_index]->ifc_name,
+ inet6_n2p(&rrt->rrt_gw),
+ rrt->rrt_info.rip6_metric, (long)age);
+ if (rrt->rrt_info.rip6_tag) {
+ fprintf(dump, " tag(0x%04x)",
+ ntohs(rrt->rrt_info.rip6_tag) & 0xffff);
+ }
+ if (rrt->rrt_flags & RTF_NH_NOT_LLADDR)
+ fprintf(dump, " NOT-LL");
+ if (rrt->rrt_flags & RTF_NOADVERTISE)
+ fprintf(dump, " NO-ADV");
+ fprintf(dump, "\n");
+ }
+ fprintf(dump, "\n");
+ if (dump != stderr)
+ fclose(dump);
+}
+
+/*
+ * Parse the -A (and -O) options and put corresponding filter object to the
+ * specified interface structures. Each of the -A/O option has the following
+ * syntax: -A 5f09:c400::/32,ef0,ef1 (aggregate)
+ * -O 5f09:c400::/32,ef0,ef1 (only when match)
+ */
+void
+filterconfig()
+{
+ int i;
+ char *p, *ap, *iflp, *ifname;
+ struct iff ftmp, *iff_obj;
+ struct ifc *ifcp;
+ struct riprt *rrt;
+ struct in6_addr gw;
+
+ for (i = 0; i < nfilter; i++) {
+ ap = filter[i];
+ iflp = NULL;
+ ifcp = NULL;
+ if (filtertype[i] == 'N' || filtertype[i] == 'T') {
+ iflp = ap;
+ goto ifonly;
+ }
+ if ((p = index(ap, ',')) != NULL) {
+ *p++ = '\0';
+ iflp = p;
+ }
+ if ((p = index(ap, '/')) == NULL)
+ fatal("no prefixlen specified for '%s'", ap);
+ *p++ = '\0';
+ if (inet_pton(AF_INET6, ap, &ftmp.iff_addr) != 1)
+ fatal("invalid prefix specified for '%s'", ap);
+ ftmp.iff_plen = atoi(p);
+ ftmp.iff_next = NULL;
+ applyplen(&ftmp.iff_addr, ftmp.iff_plen);
+ifonly:
+ ftmp.iff_type = filtertype[i];
+ if (iflp == NULL || *iflp == '\0')
+ fatal("no interface specified for '%s'", ap);
+ /* parse the interface listing portion */
+ while (iflp) {
+ ifname = iflp;
+ if ((iflp = index(iflp, ',')) != NULL)
+ *iflp++ = '\0';
+ ifcp = ifc_find(ifname);
+ if (ifcp == NULL)
+ fatal("no interface %s exists", ifname);
+ iff_obj = (struct iff *)malloc(sizeof(struct iff));
+ if (iff_obj == NULL)
+ fatal("malloc of iff_obj");
+ memcpy((void *)iff_obj, (void *)&ftmp,
+ sizeof(struct iff));
+ /* link it to the interface filter */
+ iff_obj->iff_next = ifcp->ifc_filter;
+ ifcp->ifc_filter = iff_obj;
+ }
+ if (filtertype[i] != 'A')
+ continue;
+ /* put the aggregate to the kernel routing table */
+ rrt = (struct riprt *)malloc(sizeof(struct riprt));
+ if (rrt == NULL)
+ fatal("malloc: rrt");
+ memset(rrt, 0, sizeof(struct riprt));
+ rrt->rrt_info.rip6_dest = ftmp.iff_addr;
+ rrt->rrt_info.rip6_plen = ftmp.iff_plen;
+ rrt->rrt_info.rip6_metric = 1;
+ rrt->rrt_info.rip6_tag = htons(routetag & 0xffff);
+ rrt->rrt_gw = in6addr_loopback;
+ rrt->rrt_flags = RTF_UP | RTF_REJECT | RTF_AGGREGATE;
+ rrt->rrt_t = 0;
+ rrt->rrt_index = loopifindex;
+ /* Put the route to the list */
+ rrt->rrt_next = riprt;
+ riprt = rrt;
+ trace(1, "Aggregate: %s/%d for %s\n",
+ inet6_n2p(&ftmp.iff_addr), ftmp.iff_plen,
+ ifcp->ifc_name);
+ /* Add this route to the kernel */
+ if (nflag) /* do not modify kernel routing table */
+ continue;
+ if (getroute(&rrt->rrt_info, &gw)) {
+ /*
+ * When the address has already been registered in the
+ * kernel routing table, it should be removed
+ */
+ delroute(&rrt->rrt_info, &gw);
+ }
+ addroute(rrt, &in6addr_loopback, loopifcp);
+ }
+}
+
+/***************** utility functions *****************/
+
+/*
+ * Returns a pointer to ifac whose address and prefix length matches
+ * with the address and prefix length specified in the arguments.
+ */
+struct ifac *
+ifa_match(ifcp, ia, plen)
+ const struct ifc *ifcp;
+ const struct in6_addr *ia;
+ int plen;
+{
+ struct ifac *ifa;
+
+ for (ifa = ifcp->ifc_addr; ifa; ifa = ifa->ifa_next) {
+ if (IN6_ARE_ADDR_EQUAL(&ifa->ifa_addr, ia) &&
+ ifa->ifa_plen == plen)
+ break;
+ }
+ return ifa;
+}
+
+/*
+ * Return a pointer to riprt structure whose address and prefix length
+ * matches with the address and prefix length found in the argument.
+ * Note: This is not a rtalloc(). Therefore exact match is necessary.
+ */
+
+struct riprt *
+rtsearch(np)
+ struct netinfo6 *np;
+{
+ struct riprt *rrt;
+
+ for (rrt = riprt; rrt; rrt = rrt->rrt_next) {
+ if (rrt->rrt_info.rip6_plen == np->rip6_plen &&
+ IN6_ARE_ADDR_EQUAL(&rrt->rrt_info.rip6_dest,
+ &np->rip6_dest))
+ return rrt;
+ }
+ return 0;
+}
+
+int
+mask2len(addr, lenlim)
+ const struct in6_addr *addr;
+ int lenlim;
+{
+ int i = 0, j;
+ u_char *p = (u_char *)addr;
+
+ for (j = 0; j < lenlim; j++, p++) {
+ if (*p != 0xff)
+ break;
+ i += 8;
+ }
+ if (j < lenlim) {
+ switch (*p) {
+#define MASKLEN(m, l) case m: i += l; break
+ MASKLEN(0xfe, 7);
+ MASKLEN(0xfc, 6);
+ MASKLEN(0xf8, 5);
+ MASKLEN(0xf0, 4);
+ MASKLEN(0xe0, 3);
+ MASKLEN(0xc0, 2);
+ MASKLEN(0x80, 1);
+#undef MASKLEN
+ }
+ }
+ return i;
+}
+
+void
+applymask(addr, mask)
+ struct in6_addr *addr, *mask;
+{
+ int i;
+ u_long *p, *q;
+
+ p = (u_long *)addr; q = (u_long *)mask;
+ for (i = 0; i < 4; i++)
+ *p++ &= *q++;
+}
+
+static const u_char plent[8] = {
+ 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe
+};
+
+void
+applyplen(ia, plen)
+ struct in6_addr *ia;
+ int plen;
+{
+ u_char *p;
+ int i;
+
+ p = ia->s6_addr;
+ for (i = 0; i < 16; i++) {
+ if (plen <= 0)
+ *p = 0;
+ else if (plen < 8)
+ *p &= plent[plen];
+ p++, plen -= 8;
+ }
+}
+
+static const int pl2m[9] = {
+ 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff
+};
+
+struct in6_addr *
+plen2mask(n)
+ int n;
+{
+ static struct in6_addr ia;
+ u_char *p;
+ int i;
+
+ memset(&ia, 0, sizeof(struct in6_addr));
+ p = (u_char *)&ia;
+ for (i = 0; i < 16; i++, p++, n -= 8) {
+ if (n >= 8) {
+ *p = 0xff;
+ continue;
+ }
+ *p = pl2m[n];
+ break;
+ }
+ return &ia;
+}
+
+char *
+allocopy(p)
+ char *p;
+{
+ char *q = (char *)malloc(strlen(p) + 1);
+
+ strcpy(q, p);
+ return q;
+}
+
+char *
+hms()
+{
+ static char buf[BUFSIZ];
+ time_t t;
+ struct tm *tm;
+
+ t = time(NULL);
+ if ((tm = localtime(&t)) == 0)
+ fatal("localtime");
+ snprintf(buf, sizeof(buf), "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec);
+ return buf;
+}
+
+#define RIPRANDDEV 1.0 /* 30 +- 15, max - min = 30 */
+
+int
+ripinterval(timer)
+ int timer;
+{
+ double r = rand();
+
+ interval = (int)(timer + timer * RIPRANDDEV * (r / RAND_MAX - 0.5));
+ nextalarm = time(NULL) + interval;
+ return interval;
+}
+
+time_t
+ripsuptrig()
+{
+ time_t t;
+
+ double r = rand();
+ t = (int)(RIP_TRIG_INT6_MIN +
+ (RIP_TRIG_INT6_MAX - RIP_TRIG_INT6_MIN) * (r / RAND_MAX ));
+ sup_trig_update = time(NULL) + t;
+ return t;
+}
+
+void
+#ifdef __STDC__
+fatal(const char *fmt, ...)
+#else
+fatal(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+ char buf[1024];
+
+#ifdef __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ perror(buf);
+ syslog(LOG_ERR, "%s: %s", buf, strerror(errno));
+ rtdexit(0);
+ va_end(ap);
+}
+
+void
+#ifdef __STDC__
+tracet(int level, const char *fmt, ...)
+#else
+tracet(level, fmt, va_alist)
+ int level;
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+
+#ifdef __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ if (level <= dflag) {
+ fprintf(stderr, "%s: ", hms());
+ vfprintf(stderr, fmt, ap);
+ }
+ if (dflag) {
+ if (level > 0)
+ vsyslog(LOG_DEBUG, fmt, ap);
+ else
+ vsyslog(LOG_WARNING, fmt, ap);
+ }
+ va_end(ap);
+}
+
+void
+#ifdef __STDC__
+trace(int level, const char *fmt, ...)
+#else
+trace(level, fmt, va_alist)
+ int level;
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+
+#ifdef __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ if (level <= dflag)
+ vfprintf(stderr, fmt, ap);
+ if (dflag) {
+ if (level > 0)
+ vsyslog(LOG_DEBUG, fmt, ap);
+ else
+ vsyslog(LOG_WARNING, fmt, ap);
+ }
+ va_end(ap);
+}
+
+unsigned int
+if_maxindex()
+{
+ struct if_nameindex *p, *p0;
+ unsigned int max = 0;
+
+ p0 = if_nameindex();
+ for (p = p0; p && p->if_index && p->if_name; p++) {
+ if (max < p->if_index)
+ max = p->if_index;
+ }
+ if_freenameindex(p0);
+ return max;
+}
+
+struct ifc *
+ifc_find(name)
+ char *name;
+{
+ struct ifc *ifcp;
+
+ for (ifcp = ifc; ifcp; ifcp = ifcp->ifc_next) {
+ if (strcmp(name, ifcp->ifc_name) == 0)
+ return ifcp;
+ }
+ return (struct ifc *)NULL;
+}
+
+struct iff *
+iff_find(ifcp, type)
+ struct ifc *ifcp;
+ int type;
+{
+ struct iff *iffp;
+
+ for (iffp = ifcp->ifc_filter; iffp; iffp = iffp->iff_next) {
+ if (iffp->iff_type == type)
+ return iffp;
+ }
+ return NULL;
+}
+
+void
+setindex2ifc(index, ifcp)
+ int index;
+ struct ifc *ifcp;
+{
+ int n;
+
+ if (!index2ifc) {
+ nindex2ifc = 5; /*initial guess*/
+ index2ifc = (struct ifc **)
+ malloc(sizeof(*index2ifc) * nindex2ifc);
+ if (index2ifc == NULL)
+ fatal("malloc");
+ memset(index2ifc, 0, sizeof(*index2ifc) * nindex2ifc);
+ }
+ n = nindex2ifc;
+ while (nindex2ifc <= index)
+ nindex2ifc *= 2;
+ if (n != nindex2ifc) {
+ index2ifc = (struct ifc **)
+ realloc(index2ifc, sizeof(*index2ifc) * nindex2ifc);
+ if (index2ifc == NULL)
+ fatal("realloc");
+ }
+ index2ifc[index] = ifcp;
+}
diff --git a/usr.sbin/route6d/route6d.h b/usr.sbin/route6d/route6d.h
new file mode 100644
index 0000000..7808c04
--- /dev/null
+++ b/usr.sbin/route6d/route6d.h
@@ -0,0 +1,91 @@
+/*
+ * $Header: /cvsroot/kame/kame/kame/kame/route6d/route6d.h,v 1.1.1.1 1999/08/08 23:31:35 itojun Exp $
+ */
+
+/*
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+/* not yet in use
+#define ROUTE6D_CONF "/usr/local/v6/etc/route6d.conf"
+*/
+
+#define ROUTE6D_DUMP "/var/tmp/route6d_dump"
+#define ROUTE6D_PID "/var/run/route6d.pid"
+
+#define RIP6_VERSION 1
+
+#define RIP6_REQUEST 1
+#define RIP6_RESPONSE 2
+
+struct netinfo6 {
+ struct in6_addr rip6_dest;
+ u_short rip6_tag;
+ u_char rip6_plen;
+ u_char rip6_metric;
+};
+
+struct rip6 {
+ u_char rip6_cmd;
+ u_char rip6_vers;
+ u_char rip6_res1[2];
+ union {
+ struct netinfo6 ru6_nets[1];
+ char ru6_tracefile[1];
+ } rip6un;
+#define rip6_nets rip6un.ru6_nets
+#define rip6_tracefile rip6un.ru6_tracefile
+};
+
+#define HOPCNT_INFINITY6 16
+#define NEXTHOP_METRIC 0xff
+#define RIP6_MAXMTU 1500
+
+#define IFMINMTU 576
+
+#ifndef DEBUG
+#define SUPPLY_INTERVAL6 30
+#define RIP_LIFETIME 180
+#define RIP_HOLDDOWN 120
+#define RIP_TRIG_INT6_MAX 5
+#define RIP_TRIG_INT6_MIN 1
+#else
+/* only for debugging; can not wait for 30sec to appear a bug */
+#define SUPPLY_INTERVAL6 10
+#define RIP_LIFETIME 60
+#define RIP_HOLDDOWN 40
+#define RIP_TRIG_INT6_MAX 5
+#define RIP_TRIG_INT6_MIN 1
+#endif
+
+#define RIP6_PORT 521
+#define RIP6_DEST "ff02::9"
+
+#define LOOPBACK_IF "lo0"
diff --git a/usr.sbin/rtsold/Makefile b/usr.sbin/rtsold/Makefile
new file mode 100644
index 0000000..e348b27
--- /dev/null
+++ b/usr.sbin/rtsold/Makefile
@@ -0,0 +1,25 @@
+# Copyright (c) 1996 WIDE Project. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modifications, are permitted provided that the above copyright notice
+# and this paragraph are duplicated in all such forms and that any
+# documentation, advertising materials, and other materials related to
+# such distribution and use acknowledge that the software was developed
+# by the WIDE Project, Japan. The name of the Project may not be used to
+# endorse or promote products derived from this software without
+# specific prior written permission. THIS SOFTWARE IS PROVIDED ``AS IS''
+# AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+# LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE.
+# $FreeBSD$
+
+PROG= rtsold
+SRCS= rtsold.c rtsol.c if.c probe.c dump.c
+CFLAGS+=-DINET6
+LDADD+= -lkvm
+DPADD+= ${LIBKVM}
+
+MAN8= rtsold.8
+MLINKS= rtsold.8 rtsol.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rtsold/dump.c b/usr.sbin/rtsold/dump.c
new file mode 100644
index 0000000..1587721
--- /dev/null
+++ b/usr.sbin/rtsold/dump.c
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 1999 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <netinet/in.h>
+#include <netinet/icmp6.h>
+
+#include <syslog.h>
+#include <time.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include "rtsold.h"
+
+static FILE *fp;
+
+extern struct ifinfo *iflist;
+
+static char *sec2str __P((time_t));
+char *ifstatstr[] = {"IDLE", "DELAY", "PROBE", "DOWN", "TENTATIVE"};
+
+static void
+dump_interface_status()
+{
+ struct ifinfo *ifinfo;
+ struct timeval now;
+
+ gettimeofday(&now, NULL);
+
+ for (ifinfo = iflist; ifinfo; ifinfo = ifinfo->next) {
+ fprintf(fp, "Interface %s\n", ifinfo->ifname);
+ fprintf(fp, " probe interval: ");
+ if (ifinfo->probeinterval) {
+ fprintf(fp, "%d\n", ifinfo->probeinterval);
+ fprintf(fp, " probe timer: %d\n", ifinfo->probetimer);
+ }
+ else {
+ fprintf(fp, "infinity\n");
+ fprintf(fp, " no probe timer\n");
+ }
+ fprintf(fp, " interface status: %s\n",
+ ifinfo->active > 0 ? "active" : "inactive");
+ fprintf(fp, " rtsold status: %s\n", ifstatstr[ifinfo->state]);
+ fprintf(fp, " carrier detection: %s\n",
+ ifinfo->mediareqok ? "available" : "unavailable");
+ fprintf(fp, " probes: %d, dadcount = %d\n",
+ ifinfo->probes, ifinfo->dadcount);
+ if (ifinfo->timer.tv_sec == tm_max.tv_sec &&
+ ifinfo->timer.tv_usec == tm_max.tv_usec)
+ fprintf(fp, " no timer\n");
+ else {
+ fprintf(fp, " timer: interval=%d:%d, expire=%s\n",
+ (int)ifinfo->timer.tv_sec,
+ (int)ifinfo->timer.tv_usec,
+ (ifinfo->expire.tv_sec < now.tv_sec) ? "expired"
+ : sec2str(ifinfo->expire.tv_sec - now.tv_sec));
+ }
+ fprintf(fp, " number of valid RAs: %d\n", ifinfo->racnt);
+ }
+}
+
+void
+rtsold_dump_file(dumpfile)
+ char *dumpfile;
+{
+ if ((fp = fopen(dumpfile, "w")) == NULL) {
+ warnmsg(LOG_WARNING, __FUNCTION__, "open a dump file(%s)",
+ dumpfile, strerror(errno));
+ return;
+ }
+
+ dump_interface_status();
+
+ fclose(fp);
+}
+
+static char *
+sec2str(total)
+ time_t total;
+{
+ static char result[256];
+ int days, hours, mins, secs;
+ int first = 1;
+ char *p = result;
+
+ days = total / 3600 / 24;
+ hours = (total / 3600) % 24;
+ mins = (total / 60) % 60;
+ secs = total % 60;
+
+ if (days) {
+ first = 0;
+ p += sprintf(p, "%dd", days);
+ }
+ if (!first || hours) {
+ first = 0;
+ p += sprintf(p, "%dh", hours);
+ }
+ if (!first || mins) {
+ first = 0;
+ p += sprintf(p, "%dm", mins);
+ }
+ sprintf(p, "%ds", secs);
+
+ return(result);
+}
diff --git a/usr.sbin/rtsold/if.c b/usr.sbin/rtsold/if.c
new file mode 100644
index 0000000..4ac1d4b
--- /dev/null
+++ b/usr.sbin/rtsold/if.c
@@ -0,0 +1,423 @@
+/*
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/ioctl.h>
+
+#include <net/if.h>
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+#include <net/if_var.h>
+#endif /* __FreeBSD__ >= 3 */
+#include <net/if_types.h>
+#include <net/route.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#ifdef __FreeBSD__
+# include <net/ethernet.h>
+#endif
+#ifdef __NetBSD__
+#include <net/if_ether.h>
+#endif
+#if defined(__bsdi__) || defined(__OpenBSD__)
+# include <netinet/in.h>
+# include <netinet/if_ether.h>
+#endif
+#include <netinet/in.h>
+#include <netinet/icmp6.h>
+
+#include <netinet6/in6_var.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <kvm.h>
+#include <nlist.h>
+#include <limits.h>
+
+#include "rtsold.h"
+
+static int ifsock;
+
+static int getifa __P((char *name, struct in6_ifaddr *ifap));
+static void get_rtaddrs __P((int addrs, struct sockaddr *sa,
+ struct sockaddr **rti_info));
+
+int
+ifinit()
+{
+ if ((ifsock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ warnmsg(LOG_ERR, __FUNCTION__, "socket: %s", strerror(errno));
+ return(-1);
+ }
+
+ return(0);
+}
+
+int
+interface_up(char *name)
+{
+ struct ifreq ifr;
+ struct in6_ifaddr ifa;
+
+ strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
+
+ if (ioctl(ifsock, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
+ warnmsg(LOG_WARNING, __FUNCTION__, "ioctl(SIOCGIFFLAGS): %s",
+ strerror(errno));
+ return(-1);
+ }
+ if (!(ifr.ifr_flags & IFF_UP)) {
+ ifr.ifr_flags |= IFF_UP;
+ if (ioctl(ifsock, SIOCSIFFLAGS, (caddr_t)&ifr) < 0) {
+ warnmsg(LOG_ERR, __FUNCTION__,
+ "ioctl(SIOCSIFFLAGS): %s", strerror(errno));
+ }
+ return(-1);
+ }
+
+ warnmsg(LOG_DEBUG, __FUNCTION__, "checking if %s is ready...", name);
+
+ if (getifa(name, &ifa) < 0) {
+ warnmsg(LOG_WARNING, __FUNCTION__,
+ "getifa() failed, anyway I'll try");
+ return 0;
+ }
+
+ if (!(ifa.ia6_flags & IN6_IFF_NOTREADY)) {
+ warnmsg(LOG_DEBUG, __FUNCTION__,
+ "%s is ready", name);
+ return(0);
+ }
+ else {
+ if (ifa.ia6_flags & IN6_IFF_TENTATIVE) {
+ warnmsg(LOG_DEBUG, __FUNCTION__, "%s is tentative",
+ name);
+ return IFS_TENTATIVE;
+ }
+ if (ifa.ia6_flags & IN6_IFF_DUPLICATED)
+ warnmsg(LOG_DEBUG, __FUNCTION__, "%s is duplicated",
+ name);
+ return -1;
+ }
+}
+
+int
+interface_status(struct ifinfo *ifinfo)
+{
+ char *ifname = ifinfo->ifname;
+ struct ifreq ifr;
+ struct ifmediareq ifmr;
+
+ /* get interface flags */
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ if (ioctl(ifsock, SIOCGIFFLAGS, &ifr) < 0) {
+ warnmsg(LOG_ERR, __FUNCTION__, "ioctl(SIOCGIFFLAGS) on %s: %s",
+ ifname, strerror(errno));
+ return(-1);
+ }
+ /*
+ * if one of UP and RUNNING flags is dropped,
+ * the interface is not active.
+ */
+ if ((ifr.ifr_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
+ goto inactive;
+ }
+
+ /* Next, check carrier on the interface, if possible */
+ if (!ifinfo->mediareqok)
+ goto active;
+ memset(&ifmr, 0, sizeof(ifmr));
+ strncpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
+
+ if (ioctl(ifsock, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) {
+ if (errno != EINVAL) {
+ warnmsg(LOG_DEBUG, __FUNCTION__,
+ "ioctl(SIOCGIFMEDIA) on %s: %s",
+ ifname, strerror(errno));
+ return(-1);
+ }
+ /*
+ * EINVAL simply means that the interface does not support
+ * the SIOCGIFMEDIA ioctl. We regard it alive.
+ */
+ ifinfo->mediareqok = 0;
+ goto active;
+ }
+
+ if (ifmr.ifm_status & IFM_AVALID) {
+ switch(ifmr.ifm_active & IFM_NMASK) {
+ case IFM_ETHER:
+ if (ifmr.ifm_status & IFM_ACTIVE)
+ goto active;
+ else
+ goto inactive;
+ break;
+ default:
+ goto inactive;
+ }
+ }
+
+ inactive:
+ return(0);
+
+ active:
+ return(1);
+}
+
+#define ROUNDUP(a, size) \
+ (((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a))
+
+#define NEXT_SA(ap) (ap) = (struct sockaddr *) \
+ ((caddr_t)(ap) + ((ap)->sa_len ? ROUNDUP((ap)->sa_len,\
+ sizeof(u_long)) :\
+ sizeof(u_long)))
+#define ROUNDUP8(a) (1 + (((a) - 1) | 7))
+
+int
+lladdropt_length(struct sockaddr_dl *sdl)
+{
+ switch(sdl->sdl_type) {
+ case IFT_ETHER:
+ return(ROUNDUP8(ETHER_ADDR_LEN + 2));
+ default:
+ return(0);
+ }
+}
+
+void
+lladdropt_fill(struct sockaddr_dl *sdl, struct nd_opt_hdr *ndopt)
+{
+ char *addr;
+
+ ndopt->nd_opt_type = ND_OPT_SOURCE_LINKADDR; /* fixed */
+
+ switch(sdl->sdl_type) {
+ case IFT_ETHER:
+ ndopt->nd_opt_len = (ROUNDUP8(ETHER_ADDR_LEN + 2)) >> 3;
+ addr = (char *)(ndopt + 1);
+ memcpy(addr, LLADDR(sdl), ETHER_ADDR_LEN);
+ break;
+ default:
+ warnmsg(LOG_ERR, __FUNCTION__,
+ "unsupported link type(%d)", sdl->sdl_type);
+ exit(1);
+ }
+
+ return;
+}
+
+struct sockaddr_dl *
+if_nametosdl(char *name)
+{
+ int mib[6] = {CTL_NET, AF_ROUTE, 0, 0, NET_RT_IFLIST, 0};
+ char *buf, *next, *lim;
+ size_t len;
+ struct if_msghdr *ifm;
+ struct sockaddr *sa, *rti_info[RTAX_MAX];
+ struct sockaddr_dl *sdl = NULL, *ret_sdl;
+
+ if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0)
+ return(NULL);
+ if ((buf = malloc(len)) == NULL)
+ return(NULL);
+ if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
+ free(buf);
+ return(NULL);
+ }
+
+ lim = buf + len;
+ for (next = buf; next < lim; next += ifm->ifm_msglen) {
+ ifm = (struct if_msghdr *)next;
+ if (ifm->ifm_type == RTM_IFINFO) {
+ sa = (struct sockaddr *)(ifm + 1);
+ get_rtaddrs(ifm->ifm_addrs, sa, rti_info);
+ if ((sa = rti_info[RTAX_IFP]) != NULL) {
+ if (sa->sa_family == AF_LINK) {
+ sdl = (struct sockaddr_dl *)sa;
+ if (strncmp(&sdl->sdl_data[0],
+ name,
+ sdl->sdl_nlen) == 0) {
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (next == lim) {
+ /* search failed */
+ free(buf);
+ return(NULL);
+ }
+
+ if ((ret_sdl = malloc(sdl->sdl_len)) == NULL)
+ return(NULL);
+ memcpy((caddr_t)ret_sdl, (caddr_t)sdl, sdl->sdl_len);
+
+ return(ret_sdl);
+}
+
+int
+getinet6sysctl(int code)
+{
+ int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, 0 };
+ int value;
+ size_t size;
+
+ mib[3] = code;
+ size = sizeof(value);
+ if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), &value, &size, NULL, 0) < 0)
+ return -1;
+ else
+ return value;
+}
+
+/*------------------------------------------------------------*/
+
+static struct nlist nl[] = {
+#define N_IFNET 0
+ { "_ifnet" },
+ { "" },
+};
+
+#define KREAD(x, y, z) { \
+ if (kvm_read(kvmd, (u_long)x, (void *)y, sizeof(z)) != sizeof(z)) { \
+ warnmsg(LOG_ERR, __FUNCTION__, "kvm_read failed"); \
+ goto bad; \
+ } \
+ }
+
+static int
+getifa(char *name, struct in6_ifaddr *ifap)
+{
+ u_short index;
+ kvm_t *kvmd = NULL;
+ char buf[_POSIX2_LINE_MAX];
+ struct ifnet *ifp;
+ struct ifnet ifnet;
+ struct in6_ifaddr *ifa;
+
+ if (!ifap)
+ exit(1);
+
+ index = (u_short)if_nametoindex(name);
+ if (index == 0) {
+ warnmsg(LOG_ERR, __FUNCTION__, "if_nametoindex failed for %s",
+ name);
+ goto bad;
+ }
+ if ((kvmd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, buf)) == NULL) {
+ warnmsg(LOG_ERR, __FUNCTION__, "kvm_openfiles failed");
+ goto bad;
+ }
+ if (kvm_nlist(kvmd, nl) < 0) {
+ warnmsg(LOG_ERR, __FUNCTION__, "kvm_nlist failed");
+ goto bad;
+ }
+ if (nl[N_IFNET].n_value == 0) {
+ warnmsg(LOG_ERR, __FUNCTION__, "symbol \"%s\" not found",
+ nl[N_IFNET].n_name);
+ goto bad;
+ }
+
+ KREAD(nl[N_IFNET].n_value, &ifp, struct ifnet *);
+ while (ifp) {
+ KREAD(ifp, &ifnet, struct ifnet);
+ if (ifnet.if_index == index)
+ break;
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ ifp = TAILQ_NEXT(&ifnet, if_list);
+#elif defined(__FreeBSD__) && __FreeBSD__ >= 3
+ ifp = TAILQ_NEXT(&ifnet, if_link);
+#else
+ ifp = ifnet.if_next;
+#endif
+ }
+ if (!ifp) {
+ warnmsg(LOG_ERR, __FUNCTION__, "interface \"%s\" not found",
+ name);
+ goto bad;
+ }
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ ifa = (struct in6_ifaddr *)TAILQ_FIRST(&ifnet.if_addrlist);
+#elif defined(__FreeBSD__) && __FreeBSD__ >= 3
+ ifa = (struct in6_ifaddr *)TAILQ_FIRST(&ifnet.if_addrhead);
+#else
+ ifa = (struct in6_ifaddr *)ifnet.if_addrlist;
+#endif
+ while (ifa) {
+ KREAD(ifa, ifap, *ifap);
+ if (ifap->ia_addr.sin6_family == AF_INET6
+ && IN6_IS_ADDR_LINKLOCAL(&ifap->ia_addr.sin6_addr)) {
+ kvm_close(kvmd);
+ return 0;
+ }
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ ifa = (struct in6_ifaddr *)
+ TAILQ_NEXT((struct ifaddr *)ifap, ifa_list);
+#elif defined(__FreeBSD__) && __FreeBSD__ >= 3
+ ifa = (struct in6_ifaddr *)
+ TAILQ_NEXT((struct ifaddr *)ifap, ifa_link);
+#else
+ ifa = (struct in6_ifaddr *)(((struct ifaddr *)ifap)->ifa_next);
+#endif
+ }
+ warnmsg(LOG_ERR, __FUNCTION__, "no IPv6 link-local address for %s",
+ name);
+
+ bad:
+ if (kvmd)
+ kvm_close(kvmd);
+ return -1;
+}
+
+static void
+get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
+{
+ int i;
+
+ for (i = 0; i < RTAX_MAX; i++) {
+ if (addrs & (1 << i)) {
+ rti_info[i] = sa;
+ NEXT_SA(sa);
+ }
+ else
+ rti_info[i] = NULL;
+ }
+}
diff --git a/usr.sbin/rtsold/probe.c b/usr.sbin/rtsold/probe.c
new file mode 100644
index 0000000..4b42c21
--- /dev/null
+++ b/usr.sbin/rtsold/probe.c
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <net/if.h>
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+#include <net/if_var.h>
+#endif /* __FreeBSD__ >= 3 */
+
+#include <netinet/in.h>
+#include <netinet6/in6_var.h>
+#include <netinet/icmp6.h>
+#include <netinet6/nd6.h>
+
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "rtsold.h"
+
+static struct msghdr sndmhdr;
+static struct iovec sndiov[2];
+static int probesock;
+static void sendprobe __P((struct in6_addr *addr, int ifindex));
+
+int
+probe_init()
+{
+ static u_char sndcmsgbuf[CMSG_SPACE(sizeof(struct in6_pktinfo)) +
+ CMSG_SPACE(sizeof(int))];
+
+ if ((probesock = socket(AF_INET6, SOCK_RAW, IPPROTO_NONE)) < 0) {
+ warnmsg(LOG_ERR, __FUNCTION__, "socket: %s", strerror(errno));
+ return(-1);
+ }
+
+ /* make the socket send-only */
+ if (shutdown(probesock, 0)) {
+ warnmsg(LOG_ERR, __FUNCTION__, "shutdown: %s", strerror(errno));
+ return(-1);
+ }
+
+ /* initialize msghdr for sending packets */
+ sndmhdr.msg_namelen = sizeof(struct sockaddr_in6);
+ sndmhdr.msg_iov = sndiov;
+ sndmhdr.msg_iovlen = 1;
+ sndmhdr.msg_control = (caddr_t)sndcmsgbuf;
+ sndmhdr.msg_controllen = sizeof(sndcmsgbuf);
+
+ return(0);
+}
+
+/*
+ * Probe if each router in the default router list is still alive.
+ */
+void
+defrouter_probe(int ifindex)
+{
+ struct in6_drlist dr;
+ int s, i;
+ u_char ntopbuf[INET6_ADDRSTRLEN];
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ warnmsg(LOG_ERR, __FUNCTION__, "socket: %s", strerror(errno));
+ return;
+ }
+ bzero(&dr, sizeof(dr));
+ strcpy(dr.ifname, "lo0"); /* dummy interface */
+ if (ioctl(s, SIOCGDRLST_IN6, (caddr_t)&dr) < 0) {
+ warnmsg(LOG_ERR, __FUNCTION__, "ioctl(SIOCGDRLST_IN6): %s",
+ strerror(errno));
+ goto closeandend;
+ }
+
+ for(i = 0; dr.defrouter[i].if_index && i < PRLSTSIZ; i++) {
+ if (ifindex && dr.defrouter[i].if_index == ifindex) {
+ /* sanity check */
+ if (!IN6_IS_ADDR_LINKLOCAL(&dr.defrouter[i].rtaddr)) {
+ warnmsg(LOG_ERR, __FUNCTION__,
+ "default router list contains a "
+ "non-linklocal address(%s)",
+ inet_ntop(AF_INET6,
+ &dr.defrouter[i].rtaddr,
+ ntopbuf, INET6_ADDRSTRLEN));
+ continue; /* ignore the address */
+ }
+ sendprobe(&dr.defrouter[i].rtaddr,
+ dr.defrouter[i].if_index);
+ }
+ }
+
+ closeandend:
+ close(s);
+ return;
+}
+
+static void
+sendprobe(struct in6_addr *addr, int ifindex)
+{
+ struct sockaddr_in6 sa6_probe;
+ struct in6_pktinfo *pi;
+ struct cmsghdr *cm;
+ u_char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];;
+
+ bzero(&sa6_probe, sizeof(sa6_probe));
+ sa6_probe.sin6_family = AF_INET6;
+ sa6_probe.sin6_len = sizeof(sa6_probe);
+ sa6_probe.sin6_addr = *addr;
+
+ sndmhdr.msg_name = (caddr_t)&sa6_probe;
+ sndmhdr.msg_iov[0].iov_base = NULL;
+ sndmhdr.msg_iov[0].iov_len = 0;
+
+ cm = CMSG_FIRSTHDR(&sndmhdr);
+ /* specify the outgoing interface */
+ cm->cmsg_level = IPPROTO_IPV6;
+ cm->cmsg_type = IPV6_PKTINFO;
+ cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
+ pi = (struct in6_pktinfo *)CMSG_DATA(cm);
+ memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr)); /*XXX*/
+ pi->ipi6_ifindex = ifindex;
+
+ /* specify the hop limit of the packet for safety */
+ {
+ int hoplimit = 1;
+
+ cm = CMSG_NXTHDR(&sndmhdr, cm);
+ cm->cmsg_level = IPPROTO_IPV6;
+ cm->cmsg_type = IPV6_HOPLIMIT;
+ cm->cmsg_len = CMSG_LEN(sizeof(int));
+ memcpy(CMSG_DATA(cm), &hoplimit, sizeof(int));
+ }
+
+ warnmsg(LOG_DEBUG, __FUNCTION__, "probe a router %s on %s",
+ inet_ntop(AF_INET6, addr, ntopbuf, INET6_ADDRSTRLEN),
+ if_indextoname(ifindex, ifnamebuf));
+
+ if (sendmsg(probesock, &sndmhdr, 0))
+ warnmsg(LOG_ERR, __FUNCTION__, "sendmsg on %s: %s",
+ if_indextoname(ifindex, ifnamebuf), strerror(errno));
+
+ return;
+}
diff --git a/usr.sbin/rtsold/rtsol.c b/usr.sbin/rtsold/rtsol.c
new file mode 100644
index 0000000..5b76403
--- /dev/null
+++ b/usr.sbin/rtsold/rtsol.c
@@ -0,0 +1,312 @@
+/*
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/time.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/if_dl.h>
+
+#include <netinet/in.h>
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet/icmp6.h>
+
+#include <arpa/inet.h>
+
+#include <time.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <err.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include "rtsold.h"
+
+#define ALLROUTER "ff02::2"
+
+static struct msghdr rcvmhdr;
+static struct msghdr sndmhdr;
+static struct iovec rcviov[2];
+static struct iovec sndiov[2];
+static struct sockaddr_in6 from;
+
+static int rssock;
+
+static struct sockaddr_in6 sin6_allrouters = {sizeof(sin6_allrouters), AF_INET6};
+
+int
+sockopen()
+{
+ int on;
+ struct icmp6_filter filt;
+ static u_char answer[1500];
+ static u_char rcvcmsgbuf[CMSG_SPACE(sizeof(struct in6_pktinfo)) +
+ CMSG_SPACE(sizeof(int))];
+ static u_char sndcmsgbuf[CMSG_SPACE(sizeof(struct in6_pktinfo)) +
+ CMSG_SPACE(sizeof(int))];
+
+ memset(&sin6_allrouters, 0, sizeof(struct sockaddr_in6));
+ if (inet_pton(AF_INET6, ALLROUTER,
+ &sin6_allrouters.sin6_addr.s6_addr) != 1) {
+ warnmsg(LOG_ERR, __FUNCTION__, "inet_pton failed for %s",
+ ALLROUTER);
+ return(-1);
+ }
+
+ if ((rssock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) {
+ warnmsg(LOG_ERR, __FUNCTION__, "socket: %s", strerror(errno));
+ return(-1);
+ }
+
+ /* specify to tell receiving interface */
+ on = 1;
+ if (setsockopt(rssock, IPPROTO_IPV6, IPV6_PKTINFO, &on,
+ sizeof(on)) < 0) {
+ warnmsg(LOG_ERR, __FUNCTION__, "IPV6_PKTINFO: %s",
+ strerror(errno));
+ exit(1);
+ }
+
+ on = 1;
+ /* specify to tell value of hoplimit field of received IP6 hdr */
+ if (setsockopt(rssock, IPPROTO_IPV6, IPV6_HOPLIMIT, &on,
+ sizeof(on)) < 0) {
+ warnmsg(LOG_ERR, __FUNCTION__, "IPV6_HOPLIMIT: %s",
+ strerror(errno));
+ exit(1);
+ }
+
+ /* specfiy to accept only router advertisements on the socket */
+ ICMP6_FILTER_SETBLOCKALL(&filt);
+ ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt);
+ if (setsockopt(rssock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt,
+ sizeof(filt)) == -1) {
+ warnmsg(LOG_ERR, __FUNCTION__, "setsockopt(ICMP6_FILTER): %s",
+ strerror(errno));
+ return(-1);
+ }
+
+ /* initialize msghdr for receiving packets */
+ rcviov[0].iov_base = (caddr_t)answer;
+ rcviov[0].iov_len = sizeof(answer);
+ rcvmhdr.msg_name = (caddr_t)&from;
+ rcvmhdr.msg_namelen = sizeof(from);
+ rcvmhdr.msg_iov = rcviov;
+ rcvmhdr.msg_iovlen = 1;
+ rcvmhdr.msg_control = (caddr_t) rcvcmsgbuf;
+ rcvmhdr.msg_controllen = sizeof(rcvcmsgbuf);
+
+ /* initialize msghdr for sending packets */
+ sndmhdr.msg_namelen = sizeof(struct sockaddr_in6);
+ sndmhdr.msg_iov = sndiov;
+ sndmhdr.msg_iovlen = 1;
+ sndmhdr.msg_control = (caddr_t)sndcmsgbuf;
+ sndmhdr.msg_controllen = sizeof(sndcmsgbuf);
+
+ return(rssock);
+}
+
+void
+sendpacket(struct ifinfo *ifinfo)
+{
+ int i;
+ struct cmsghdr *cm;
+ struct in6_pktinfo *pi;
+
+ sndmhdr.msg_name = (caddr_t)&sin6_allrouters;
+ sndmhdr.msg_iov[0].iov_base = (caddr_t)ifinfo->rs_data;
+ sndmhdr.msg_iov[0].iov_len = ifinfo->rs_datalen;
+
+ cm = CMSG_FIRSTHDR(&sndmhdr);
+ /* specify the outgoing interface */
+ cm->cmsg_level = IPPROTO_IPV6;
+ cm->cmsg_type = IPV6_PKTINFO;
+ cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
+ pi = (struct in6_pktinfo *)CMSG_DATA(cm);
+ memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr)); /*XXX*/
+ pi->ipi6_ifindex = ifinfo->sdl->sdl_index;
+
+ /* specify the hop limit of the packet */
+ {
+ int hoplimit = 255;
+
+ cm = CMSG_NXTHDR(&sndmhdr, cm);
+ cm->cmsg_level = IPPROTO_IPV6;
+ cm->cmsg_type = IPV6_HOPLIMIT;
+ cm->cmsg_len = CMSG_LEN(sizeof(int));
+ memcpy(CMSG_DATA(cm), &hoplimit, sizeof(int));
+ }
+
+ warnmsg(LOG_DEBUG,
+ __FUNCTION__, "send RS on %s, whose state is %d",
+ ifinfo->ifname, ifinfo->state);
+
+ i = sendmsg(rssock, &sndmhdr, 0);
+
+ if (i < 0 || i != ifinfo->rs_datalen) {
+ /*
+ * ENETDOWN is not so serious, especially when using several
+ * network cards on a mobile node. We ignore it.
+ */
+ if (errno != ENETDOWN || dflag > 0)
+ warnmsg(LOG_ERR, __FUNCTION__, "sendmsg on %s: %s",
+ ifinfo->ifname, strerror(errno));
+ }
+
+ /* update counter */
+ ifinfo->probes++;
+}
+
+void
+rtsol_input(int s)
+{
+ int i;
+ int *hlimp = NULL;
+ struct icmp6_hdr *icp;
+ int ifindex = 0;
+ struct cmsghdr *cm;
+ struct in6_pktinfo *pi = NULL;
+ struct ifinfo *ifi = NULL;
+ u_char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
+
+ /* get message */
+ if ((i = recvmsg(s, &rcvmhdr, 0)) < 0) {
+ warnmsg(LOG_ERR, __FUNCTION__, "recvmsg: %s", strerror(errno));
+ return;
+ }
+
+ /* extract optional information via Advanced API */
+ for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&rcvmhdr);
+ cm;
+ cm = (struct cmsghdr *)CMSG_NXTHDR(&rcvmhdr, cm)) {
+ if (cm->cmsg_level == IPPROTO_IPV6 &&
+ cm->cmsg_type == IPV6_PKTINFO &&
+ cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) {
+ pi = (struct in6_pktinfo *)(CMSG_DATA(cm));
+ ifindex = pi->ipi6_ifindex;
+ }
+ if (cm->cmsg_level == IPPROTO_IPV6 &&
+ cm->cmsg_type == IPV6_HOPLIMIT &&
+ cm->cmsg_len == CMSG_LEN(sizeof(int)))
+ hlimp = (int *)CMSG_DATA(cm);
+ }
+
+ if (ifindex == 0) {
+ warnmsg(LOG_ERR,
+ __FUNCTION__, "failed to get receiving interface");
+ return;
+ }
+ if (hlimp == NULL) {
+ warnmsg(LOG_ERR,
+ __FUNCTION__, "failed to get receiving hop limit");
+ return;
+ }
+
+ if (i < sizeof(struct nd_router_advert)) {
+ warnmsg(LOG_ERR,
+ __FUNCTION__, "packet size(%d) is too short", i);
+ return;
+ }
+
+ icp = (struct icmp6_hdr *)rcvmhdr.msg_iov[0].iov_base;
+
+ if (icp->icmp6_type != ND_ROUTER_ADVERT) {
+ warnmsg(LOG_ERR, __FUNCTION__,
+ "invalid icmp type(%d) from %s on %s", icp->icmp6_type,
+ inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
+ INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+
+ if (icp->icmp6_code != 0) {
+ warnmsg(LOG_ERR, __FUNCTION__,
+ "invalid icmp code(%d) from %s on %s", icp->icmp6_code,
+ inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
+ INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+
+ if (*hlimp != 255) {
+ warnmsg(LOG_NOTICE, __FUNCTION__,
+ "invalid RA with hop limit(%d) from %s on %s",
+ *hlimp,
+ inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
+ INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+
+ if (pi && !IN6_IS_ADDR_LINKLOCAL(&from.sin6_addr)) {
+ warnmsg(LOG_NOTICE, __FUNCTION__,
+ "invalid RA with non link-local source from %s on %s",
+ inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
+ INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+
+ /* xxx: more validation? */
+
+ if ((ifi = find_ifinfo(pi->ipi6_ifindex)) == NULL) {
+ warnmsg(LOG_NOTICE, __FUNCTION__,
+ "received RA from %s on an unexpeced IF(%s)",
+ inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
+ INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+
+ warnmsg(LOG_DEBUG, __FUNCTION__,
+ "received RA from %s on %s, state is %d",
+ inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
+ INET6_ADDRSTRLEN),
+ ifi->ifname, ifi->state);
+
+ ifi->racnt++;
+
+ switch(ifi->state) {
+ case IFS_IDLE: /* should be ignored */
+ case IFS_DELAY: /* right? */
+ break;
+ case IFS_PROBE:
+ ifi->state = IFS_IDLE;
+ ifi->probes = 0;
+ rtsol_timer_update(ifi);
+ break;
+ }
+}
diff --git a/usr.sbin/rtsold/rtsold.8 b/usr.sbin/rtsold/rtsold.8
new file mode 100644
index 0000000..90d483a
--- /dev/null
+++ b/usr.sbin/rtsold/rtsold.8
@@ -0,0 +1,175 @@
+.\" 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.
+.\"
+.\" $Id: rtsold.8,v 1.4 1999/09/30 00:57:15 jinmei Exp $
+.\" $FreeBSD$
+.\"
+.Dd May 17, 1998
+.Dt RTSOLD 8
+.Os KAME
+.\"
+.Sh NAME
+.Nm rtsold
+.Nd router solicitation daemon
+.\"
+.Sh SYNOPSIS
+.Nm
+.Op Fl dDfm1
+.Ar interface ...
+.Nm rtsol
+.Op Fl dD
+.Ar interface ...
+.\"
+.Sh DESCRIPTION
+.Nm Rtsold
+is the daemon program to send ICMPv6 Router Solicitation messages
+on the specified interfaces.
+If a node (re)attaches to a link,
+.Nm
+sends some Router Solicitations on the link destined to the link-local scope
+all-routers multicast address to discover new routers
+and to get non link-local addresses.
+.Lp
+Specifically,
+.Nm
+sends at most 3 Router Solicitations on an interface
+after one of the following events:
+.Bl -bullet -compact
+.It
+Just after invocation of
+.Nm
+daemon.
+.It
+The interface is up after a temporary interface failure.
+.Nm Rtsold
+detects it by periodically probing if the status of the
+interface is active or not.
+Note that some network cards and drivers do not allow users
+to extract link state.
+In such cases,
+.Nm
+cannot detect the change of the interface status.
+.It
+Every one minute if
+.Fl m
+option is specified and
+.Nm
+daemon cannot get the interface status.
+This feature does not conform to IPv6 neighbor discovery
+specification, but is provided for mobile stations.
+Default interval of router advertisements, which is on the order of 10
+minutes, is slightly long for mobile stations.
+This feature is provided
+for such stations so that they can find new routers as soon as possible
+when they attach another link.
+.El
+.Lp
+Once
+.Nm
+sends a Router Solicitation, and receives a valid Router Advertisement,
+it desists from sending additional solicitations on that interface, until
+the next time one of the above events occurs.
+.Lp
+When sending a Router Solicitation on an interface,
+.Nm
+includes a Source Link-layer address option if the interface
+has its link-layer address.
+.Pp
+Upon receipt of signal
+.Dv SIGUSR1 ,
+.Nm
+will dump the current internal state into
+.Pa /var/tmp/rtsold.dump.
+.\"
+.Sh OPTIONS
+.Bl -tag -width indent
+.\"
+.It Fl d
+Enable debugging.
+.It Fl D
+Enable more debugging including to print internal timer information.
+.It Fl f
+.Fl f
+prevents
+.Nm
+from becoming a daemon (foreground mode).
+Warning messages are generated to standard error output,
+instead of
+.Xr syslog 3 .
+.It Fl m
+Enable mobility support.
+If this option is specified,
+.Nm
+sends probing packets to default routers that have advertised Router
+Advertisements
+when the node (re)attaches to an interface.
+Moreover, if the option is specified,
+.Nm
+periodically sends Router Solicitation on an interface that does not support
+.Dv SIOCGIFMEDIA
+ioctl.
+.It Fl 1
+Perform only one probe.
+Transmit Router Solcitation packet until valid Router Advertisement packet
+arrives all the interfaces more than once, then exit.
+.El
+.Pp
+If you invoke the program as
+.Nm rtsol ,
+it will behave as
+.Do
+.Nm
+.Fl f1
+.Ar interfaces
+.Dc .
+.Sh RETURN VALUES
+The program exits with 0 on success, non-zero on failures.
+.\"
+.Sh FILES
+.Bl -tag -width /var/run/rtsold.dump -compact
+.It Pa /var/run/rtsold.pid
+the pid of the currently running
+.Nm rtsold .
+.It Pa /var/tmp/rtsold.dump
+dumps internal state on.
+.El
+.\"
+.Sh SEE ALSO
+.Xr rtadvd 8 ,
+.Xr sysctl 8
+.\"
+.Sh HISTORY
+The
+.Nm
+command is based on
+.Nm rtsol
+command, which first appeared in WIDE/KAME IPv6 protocol stack kit.
+.Nm rtsol
+is now integrated into
+.Xr rtsold 8 .
+.\" .Sh BUGS
+.\" (to be written)
diff --git a/usr.sbin/rtsold/rtsold.c b/usr.sbin/rtsold/rtsold.c
new file mode 100644
index 0000000..8d5646f
--- /dev/null
+++ b/usr.sbin/rtsold/rtsold.c
@@ -0,0 +1,622 @@
+/*
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <net/if_dl.h>
+
+#include <netinet/in.h>
+#include <netinet/icmp6.h>
+
+#include <signal.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <err.h>
+#include <stdarg.h>
+#include "rtsold.h"
+
+struct ifinfo *iflist;
+struct timeval tm_max = {0x7fffffff, 0x7fffffff};
+int dflag;
+static int log_upto = 999;
+static int fflag = 0;
+
+/* protocol constatns */
+#define MAX_RTR_SOLICITATION_DELAY 1 /* second */
+#define RTR_SOLICITATION_INTERVAL 4 /* seconds */
+#define MAX_RTR_SOLICITATIONS 3 /* times */
+
+/* implementation dependent constants */
+#define PROBE_INTERVAL 60 /* secondes XXX: should be configurable */
+
+/* utility macros */
+/* a < b */
+#define TIMEVAL_LT(a, b) (((a).tv_sec < (b).tv_sec) ||\
+ (((a).tv_sec == (b).tv_sec) && \
+ ((a).tv_usec < (b).tv_usec)))
+
+/* a <= b */
+#define TIMEVAL_LEQ(a, b) (((a).tv_sec < (b).tv_sec) ||\
+ (((a).tv_sec == (b).tv_sec) &&\
+ ((a).tv_usec <= (b).tv_usec)))
+
+/* a == b */
+#define TIMEVAL_EQ(a, b) (((a).tv_sec==(b).tv_sec) && ((a).tv_usec==(b).tv_usec))
+
+int main __P((int argc, char *argv[]));
+
+/* static variables and functions */
+static int mobile_node = 0;
+static int do_dump;
+static char *dumpfilename = "/var/tmp/rtsold.dump"; /* XXX: should be configurable */
+static char *pidfilename = "/var/run/rtsold.pid"; /* should be configurable */
+
+static int ifconfig __P((char *ifname));
+static int make_packet __P((struct ifinfo *ifinfo));
+static struct timeval *rtsol_check_timer __P((void));
+static void TIMEVAL_ADD __P((struct timeval *a, struct timeval *b,
+ struct timeval *result));
+static void TIMEVAL_SUB __P((struct timeval *a, struct timeval *b,
+ struct timeval *result));
+
+static void rtsold_set_dump_file __P(());
+static void usage __P((char *progname));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int s, ch;
+ int once = 0;
+ struct timeval *timeout;
+ struct fd_set fdset;
+ char *argv0;
+ char *opts;
+
+ /*
+ * Initialization
+ */
+ argv0 = argv[0];
+
+ /* get option */
+ if (argv0 && argv0[strlen(argv0) - 1] != 'd') {
+ fflag = 1;
+ once = 1;
+ opts = "dD";
+ } else
+ opts = "dDfm1";
+
+ while ((ch = getopt(argc, argv, opts)) != -1) {
+ switch(ch) {
+ case 'd':
+ dflag = 1;
+ break;
+ case 'D':
+ dflag = 2;
+ break;
+ case 'f':
+ fflag = 1;
+ break;
+ case 'm':
+ mobile_node = 1;
+ break;
+ case '1':
+ once = 1;
+ break;
+ default:
+ usage(argv0);
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc == 0)
+ usage(argv0);
+
+ /* set log level */
+ if (dflag == 0)
+ log_upto = LOG_NOTICE;
+ if (!fflag) {
+ char *ident;
+ ident = strrchr(argv0, '/');
+ if (!ident)
+ ident = argv0;
+ else
+ ident++;
+ openlog(ident, LOG_NDELAY|LOG_PID, LOG_DAEMON);
+ if (log_upto >= 0)
+ setlogmask(LOG_UPTO(log_upto));
+ }
+
+ /* random value initilization */
+ srandom((u_long)time(NULL));
+
+ /* warn if accept_rtadv is down */
+ if (!getinet6sysctl(IPV6CTL_ACCEPT_RTADV))
+ warnx("kernel is configured not to accept RAs");
+
+ /* initialization to dump internal status to a file */
+ if (signal(SIGUSR1, (void *)rtsold_set_dump_file) < 0)
+ errx(1, "failed to set signal for dump status");
+
+ /* configuration per interface */
+ if (ifinit())
+ errx(1, "failed to initilizatoin interfaces");
+ while (argc--) {
+ if (ifconfig(*argv))
+ errx(1, "failed to initilize %s", *argv);
+ argv++;
+ }
+
+ /* open a socket for sending RS and receiving RA */
+ if ((s = sockopen()) < 0)
+ errx(1, "failed to open a socket");
+
+ /* setup for probing default routers */
+ if (probe_init())
+ errx(1, "failed to setup for probing routers");
+
+ if (!fflag)
+ daemon(0, 0); /* act as a daemon */
+
+ /* dump the current pid */
+ if (!once) {
+ pid_t pid = getpid();
+ FILE *fp;
+
+ if ((fp = fopen(pidfilename, "w")) == NULL)
+ warnmsg(LOG_ERR, __FUNCTION__,
+ "failed to open a log file(%s)",
+ pidfilename, strerror(errno));
+ else {
+ fprintf(fp, "%d\n", pid);
+ fclose(fp);
+ }
+ }
+
+ FD_ZERO(&fdset);
+ FD_SET(s, &fdset);
+ while (1) { /* main loop */
+ extern int errno;
+ int e;
+ struct fd_set select_fd = fdset;
+
+ if (do_dump) { /* SIGUSR1 */
+ do_dump = 0;
+ rtsold_dump_file(dumpfilename);
+ }
+
+ timeout = rtsol_check_timer();
+
+ if (once) {
+ struct ifinfo *ifi;
+
+ /* if we have no timeout, we are done (or failed) */
+ if (timeout == NULL)
+ break;
+
+ /* if all interfaces have got RA packet, we are done */
+ for (ifi = iflist; ifi; ifi = ifi->next) {
+ if (ifi->state != IFS_DOWN && ifi->racnt == 0)
+ break;
+ }
+ if (ifi == NULL)
+ break;
+ }
+
+ if ((e = select(s + 1, &select_fd, NULL, NULL, timeout)) < 1) {
+ if (e < 0 && errno != EINTR) {
+ warnmsg(LOG_ERR, __FUNCTION__, "select: %s",
+ strerror(errno));
+ }
+ continue;
+ }
+
+ /* packet reception */
+ if (FD_ISSET(s, &fdset))
+ rtsol_input(s);
+ }
+ /* NOTREACHED */
+
+ return 0;
+}
+
+static int
+ifconfig(char *ifname)
+{
+ struct ifinfo *ifinfo;
+ struct sockaddr_dl *sdl;
+ int flags;
+
+ if ((sdl = if_nametosdl(ifname)) == NULL) {
+ warnmsg(LOG_ERR, __FUNCTION__,
+ "failed to get link layer information for %s", ifname);
+ return(-1);
+ }
+ if (find_ifinfo(sdl->sdl_index)) {
+ warnmsg(LOG_ERR, __FUNCTION__,
+ "interface %s was already cofigured", ifname);
+ return(-1);
+ }
+
+ if ((ifinfo = malloc(sizeof(*ifinfo))) == NULL) {
+ warnmsg(LOG_ERR, __FUNCTION__, "memory allocation failed");
+ return(-1);
+ }
+ memset(ifinfo, 0, sizeof(*ifinfo));
+ ifinfo->sdl = sdl;
+
+ strncpy(ifinfo->ifname, ifname, sizeof(ifinfo->ifname));
+
+ /* construct a router solicitation message */
+ if (make_packet(ifinfo))
+ goto bad;
+
+ /*
+ * check if the interface is available.
+ * also check if SIOCGIFMEDIA ioctl is OK on the interface.
+ */
+ ifinfo->mediareqok = 1;
+ ifinfo->active = interface_status(ifinfo);
+ if (!ifinfo->mediareqok) {
+ /*
+ * probe routers periodically even if the link status
+ * does not change.
+ */
+ ifinfo->probeinterval = PROBE_INTERVAL;
+ }
+
+ /* activate interface: interface_up returns 0 on success */
+ flags = interface_up(ifinfo->ifname);
+ if (flags == 0)
+ ifinfo->state = IFS_DELAY;
+ else if (flags == IFS_TENTATIVE)
+ ifinfo->state = IFS_TENTATIVE;
+ else
+ ifinfo->state = IFS_DOWN;
+
+ rtsol_timer_update(ifinfo);
+
+ /* link into chain */
+ if (iflist)
+ ifinfo->next = iflist;
+ iflist = ifinfo;
+
+ return(0);
+
+ bad:
+ free(ifinfo);
+ free(ifinfo->sdl);
+ return(-1);
+}
+
+struct ifinfo *
+find_ifinfo(int ifindex)
+{
+ struct ifinfo *ifi;
+
+ for (ifi = iflist; ifi; ifi = ifi->next)
+ if (ifi->sdl->sdl_index == ifindex)
+ return(ifi);
+
+ return(NULL);
+}
+
+static int
+make_packet(struct ifinfo *ifinfo)
+{
+ char *buf;
+ struct nd_router_solicit *rs;
+ size_t packlen = sizeof(struct nd_router_solicit), lladdroptlen = 0;
+
+ if ((lladdroptlen = lladdropt_length(ifinfo->sdl)) == 0) {
+ warnmsg(LOG_INFO, __FUNCTION__,
+ "link-layer address option has null length"
+ " on %s. Treat as not included.", ifinfo->ifname);
+ }
+ packlen += lladdroptlen;
+ ifinfo->rs_datalen = packlen;
+
+ /* allocate buffer */
+ if ((buf = malloc(packlen)) == NULL) {
+ warnmsg(LOG_ERR, __FUNCTION__,
+ "memory allocation failed for %s", ifinfo->ifname);
+ return(-1);
+ }
+ ifinfo->rs_data = buf;
+
+ /* fill in the message */
+ rs = (struct nd_router_solicit *)buf;
+ rs->nd_rs_type = ND_ROUTER_SOLICIT;
+ rs->nd_rs_code = 0;
+ rs->nd_rs_cksum = 0;
+ rs->nd_rs_reserved = 0;
+ buf += sizeof(*rs);
+
+ /* fill in source link-layer address option */
+ if (lladdroptlen)
+ lladdropt_fill(ifinfo->sdl, (struct nd_opt_hdr *)buf);
+
+ return(0);
+}
+
+static struct timeval *
+rtsol_check_timer()
+{
+ static struct timeval returnval;
+ struct timeval now, rtsol_timer;
+ struct ifinfo *ifinfo;
+ int flags;
+
+ gettimeofday(&now, NULL);
+
+ rtsol_timer = tm_max;
+
+ for (ifinfo = iflist; ifinfo; ifinfo = ifinfo->next) {
+ if (TIMEVAL_LEQ(ifinfo->expire, now)) {
+ if (dflag > 1)
+ warnmsg(LOG_DEBUG, __FUNCTION__,
+ "timer expiration on %s, "
+ "state = %d", ifinfo->ifname,
+ ifinfo->state);
+
+ switch(ifinfo->state) {
+ case IFS_DOWN:
+ case IFS_TENTATIVE:
+ /* interface_up returns 0 on success */
+ flags = interface_up(ifinfo->ifname);
+ if (flags == 0)
+ ifinfo->state = IFS_DELAY;
+ else if (flags == IFS_TENTATIVE)
+ ifinfo->state = IFS_TENTATIVE;
+ else
+ ifinfo->state = IFS_DOWN;
+ break;
+ case IFS_IDLE:
+ {
+ int oldstatus = ifinfo->active;
+ int probe = 0;
+
+ ifinfo->active =
+ interface_status(ifinfo);
+
+ if (oldstatus != ifinfo->active) {
+ warnmsg(LOG_DEBUG, __FUNCTION__,
+ "%s status is changed"
+ " from %d to %d",
+ ifinfo->ifname,
+ oldstatus, ifinfo->active);
+ probe = 1;
+ ifinfo->state = IFS_DELAY;
+ }
+ else if (ifinfo->probeinterval &&
+ (ifinfo->probetimer -=
+ ifinfo->timer.tv_sec) <= 0) {
+ /* probe timer expired */
+ ifinfo->probetimer =
+ ifinfo->probeinterval;
+ probe = 1;
+ ifinfo->state = IFS_PROBE;
+ }
+
+ if (probe && mobile_node)
+ defrouter_probe(ifinfo->sdl->sdl_index);
+ break;
+ }
+ case IFS_DELAY:
+ ifinfo->state = IFS_PROBE;
+ sendpacket(ifinfo);
+ break;
+ case IFS_PROBE:
+ if (ifinfo->probes < MAX_RTR_SOLICITATIONS)
+ sendpacket(ifinfo);
+ else {
+ warnmsg(LOG_INFO, __FUNCTION__,
+ "No answer "
+ "after sending %d RSs",
+ ifinfo->probes);
+ ifinfo->probes = 0;
+ ifinfo->state = IFS_IDLE;
+ }
+ break;
+ }
+ rtsol_timer_update(ifinfo);
+ }
+
+ if (TIMEVAL_LT(ifinfo->expire, rtsol_timer))
+ rtsol_timer = ifinfo->expire;
+ }
+
+ if (TIMEVAL_EQ(rtsol_timer, tm_max)) {
+ warnmsg(LOG_DEBUG, __FUNCTION__, "there is no timer");
+ return(NULL);
+ }
+ else if (TIMEVAL_LT(rtsol_timer, now))
+ /* this may occur when the interval is too small */
+ returnval.tv_sec = returnval.tv_usec = 0;
+ else
+ TIMEVAL_SUB(&rtsol_timer, &now, &returnval);
+
+ if (dflag > 1)
+ warnmsg(LOG_DEBUG, __FUNCTION__, "New timer is %d:%08d",
+ returnval.tv_sec, returnval.tv_usec);
+
+ return(&returnval);
+}
+
+void
+rtsol_timer_update(struct ifinfo *ifinfo)
+{
+#define MILLION 1000000
+#define DADRETRY 10 /* XXX: adhoc */
+ long interval;
+ struct timeval now;
+
+ bzero(&ifinfo->timer, sizeof(ifinfo->timer));
+
+ switch (ifinfo->state) {
+ case IFS_DOWN:
+ case IFS_TENTATIVE:
+ if (++ifinfo->dadcount > DADRETRY) {
+ ifinfo->dadcount = 0;
+ ifinfo->timer.tv_sec = PROBE_INTERVAL;
+ }
+ else
+ ifinfo->timer.tv_sec = 1;
+ break;
+ case IFS_IDLE:
+ if (mobile_node) {
+ /* XXX should be configurable */
+ ifinfo->timer.tv_sec = 3;
+ }
+ else
+ ifinfo->timer = tm_max; /* stop timer(valid?) */
+ break;
+ case IFS_DELAY:
+ interval = random() % (MAX_RTR_SOLICITATION_DELAY * MILLION);
+ ifinfo->timer.tv_sec = interval / MILLION;
+ ifinfo->timer.tv_usec = interval % MILLION;
+ break;
+ case IFS_PROBE:
+ ifinfo->timer.tv_sec = RTR_SOLICITATION_INTERVAL;
+ break;
+ default:
+ warnmsg(LOG_ERR, __FUNCTION__,
+ "illegal interface state(%d) on %s",
+ ifinfo->state, ifinfo->ifname);
+ return;
+ }
+
+ /* reset the timer */
+ if (TIMEVAL_EQ(ifinfo->timer, tm_max)) {
+ ifinfo->expire = tm_max;
+ warnmsg(LOG_DEBUG, __FUNCTION__,
+ "stop timer for %s", ifinfo->ifname);
+ }
+ else {
+ gettimeofday(&now, NULL);
+ TIMEVAL_ADD(&now, &ifinfo->timer, &ifinfo->expire);
+
+ if (dflag > 1)
+ warnmsg(LOG_DEBUG, __FUNCTION__,
+ "set timer for %s to %d:%d", ifinfo->ifname,
+ (int)ifinfo->timer.tv_sec,
+ (int)ifinfo->timer.tv_usec);
+ }
+
+#undef MILLION
+}
+
+/* timer related utility functions */
+#define MILLION 1000000
+
+/* result = a + b */
+static void
+TIMEVAL_ADD(struct timeval *a, struct timeval *b, struct timeval *result)
+{
+ long l;
+
+ if ((l = a->tv_usec + b->tv_usec) < MILLION) {
+ result->tv_usec = l;
+ result->tv_sec = a->tv_sec + b->tv_sec;
+ }
+ else {
+ result->tv_usec = l - MILLION;
+ result->tv_sec = a->tv_sec + b->tv_sec + 1;
+ }
+}
+
+/*
+ * result = a - b
+ * XXX: this function assumes that a >= b.
+ */
+void
+TIMEVAL_SUB(struct timeval *a, struct timeval *b, struct timeval *result)
+{
+ long l;
+
+ if ((l = a->tv_usec - b->tv_usec) >= 0) {
+ result->tv_usec = l;
+ result->tv_sec = a->tv_sec - b->tv_sec;
+ }
+ else {
+ result->tv_usec = MILLION + l;
+ result->tv_sec = a->tv_sec - b->tv_sec - 1;
+ }
+}
+
+static void
+rtsold_set_dump_file()
+{
+ do_dump = 1;
+}
+
+static void
+usage(char *progname)
+{
+ if (progname && progname[strlen(progname) - 1] != 'd')
+ fprintf(stderr, "usage: rtsol [-dD] interfaces\n");
+ else
+ fprintf(stderr, "usage: rtsold [-dDfm1] interfaces\n");
+ exit(1);
+}
+
+void
+#if __STDC__
+warnmsg(int priority, const char *func, const char *msg, ...)
+#else
+warnmsg(priority, func, msg, va_alist)
+ int priority;
+ const char *func;
+ const char *msg;
+ va_dcl
+#endif
+{
+ va_list ap;
+ char buf[BUFSIZ];
+
+ va_start(ap, msg);
+ if (fflag) {
+ if (priority <= log_upto) {
+ (void)vfprintf(stderr, msg, ap);
+ (void)fprintf(stderr, "\n");
+ }
+ } else {
+ snprintf(buf, sizeof(buf), "<%s> %s", func, msg);
+ vsyslog(priority, buf, ap);
+ }
+ va_end(ap);
+}
diff --git a/usr.sbin/rtsold/rtsold.h b/usr.sbin/rtsold/rtsold.h
new file mode 100644
index 0000000..afb5088
--- /dev/null
+++ b/usr.sbin/rtsold/rtsold.h
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+struct ifinfo {
+ struct ifinfo *next; /* pointer to the next interface */
+
+ struct sockaddr_dl *sdl; /* link-layer address */
+ char ifname[16]; /* interface name */
+ int active; /* interface status */
+ int probeinterval; /* interval of probe timer(if necessary) */
+ int probetimer; /* rest of probe timer */
+ int mediareqok; /* wheter the IF supports SIOCGIFMEDIA */
+ int state;
+ int probes;
+ int dadcount;
+ struct timeval timer;
+ struct timeval expire;
+
+ int racnt; /* total # of valid RAs it have got */
+
+ size_t rs_datalen;
+ u_char *rs_data;
+};
+
+/* per interface status */
+#define IFS_IDLE 0
+#define IFS_DELAY 1
+#define IFS_PROBE 2
+#define IFS_DOWN 3
+#define IFS_TENTATIVE 4
+
+/* rtsold.c */
+extern struct timeval tm_max;
+extern int dflag;
+struct ifinfo *find_ifinfo __P((int ifindex));
+void rtsol_timer_update __P((struct ifinfo *ifinfo));
+#ifdef __STDC__
+extern void warnmsg __P((int, const char *, const char *, ...));
+#else
+extern void warnmsg __P((int, const char *, const char *, va_list));
+#endif
+
+/* if.c */
+extern int ifinit __P((void));
+extern int interface_up __P((char *name));
+extern int interface_status __P((struct ifinfo*));
+extern int lladdropt_length __P((struct sockaddr_dl *sdl));
+extern void lladdropt_fill __P((struct sockaddr_dl *sdl,
+ struct nd_opt_hdr *ndopt));
+extern struct sockaddr_dl *if_nametosdl __P((char *name));
+extern int getinet6sysctl __P((int code));
+
+/* rtsol.c */
+extern int sockopen __P((void));
+extern void sendpacket __P((struct ifinfo *ifinfo));
+extern void rtsol_input __P((int s));
+
+/* probe.c */
+extern int probe_init __P((void));
+extern void defrouter_probe __P((int ifindex));
+
+/* dump.c */
+extern void rtsold_dump_file __P((char *));
diff --git a/usr.sbin/traceroute6/Makefile b/usr.sbin/traceroute6/Makefile
new file mode 100644
index 0000000..2b151a6
--- /dev/null
+++ b/usr.sbin/traceroute6/Makefile
@@ -0,0 +1,26 @@
+# Copyright (c) 1996 WIDE Project. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modifications, are permitted provided that the above copyright notice
+# and this paragraph are duplicated in all such forms and that any
+# documentation, advertising materials, and other materials related to
+# such distribution and use acknowledge that the software was developed
+# by the WIDE Project, Japan. The name of the Project may not be used to
+# endorse or promote products derived from this software without
+# specific prior written permission. THIS SOFTWARE IS PROVIDED ``AS IS''
+# AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+# LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE.
+# $FreeBSD$
+
+PROG= traceroute6
+
+BINOWN= root
+BINGRP= bin
+BINMODE=4555
+
+CFLAGS+=-DINET6
+
+MAN8= traceroute6.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/traceroute6/traceroute6.8 b/usr.sbin/traceroute6/traceroute6.8
new file mode 100644
index 0000000..b98773f
--- /dev/null
+++ b/usr.sbin/traceroute6/traceroute6.8
@@ -0,0 +1,96 @@
+.\" 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.
+.\"
+.\" $Id: traceroute6.8,v 1.1.1.1 1999/08/08 23:32:38 itojun Exp $
+.\" $FreeBSD$
+.\"
+.Dd May 17, 1998
+.Dt TRACEROUTE6 8
+.Os KAME
+.\"
+.Sh NAME
+.Nm traceroute6
+.Nd print the route IPv6 packets will take to the destination
+.\"
+.Sh SYNOPSIS
+.Nm
+.Op Fl dlnrv
+.Op Fl m Ar hoplimit
+.Op Fl p Ar port
+.Op Fl q Ar probes
+.Op Fl s Ar src
+.Op Fl w Ar waittime
+.\"
+.Sh DESCRIPTION
+.Bl -tag -width Ds
+.It Fl d
+Debug mode.
+.It Fl m Ar hoplimit
+Specify maximum hoplimit.
+.It Fl l
+Print both host hostnames and numeric addresses.
+Normally
+.Nm
+prints only hostnames if
+.Fl n
+is not specified, and only numeric addresses if
+.Fl n
+is specified.
+.It Fl n
+Do not resolve numeric address to hostname.
+.It Fl p Ar port
+Set UDP port number to
+.Ar port .
+.It Fl q Ar probes
+Set the number of probe per hop count to
+.Ar probes .
+.It Fl r
+.It Fl s Ar src
+.Ar Src
+specifies the source IPv6 address to be used.
+.It Fl v
+Be verbose.
+.It Fl w Ar waittime
+Specify the delay time between probes.
+.El
+.\"
+.Sh RETURN VALUES
+.Nm Traceroute6
+will exit with 0 on success, and non-zero on errors.
+.\"
+.Sh SEE ALSO
+.Xr ping 8 ,
+.Xr ping6 8 ,
+.Xr traceroute 8
+.\"
+.Sh HISTORY
+The
+.Nm
+command first appeared in WIDE hydrangea IPv6 protocol stack kit.
+.\"
+.\" .Sh BUGS
+.\" (to be written)
diff --git a/usr.sbin/traceroute6/traceroute6.c b/usr.sbin/traceroute6/traceroute6.c
new file mode 100644
index 0000000..a1b665e
--- /dev/null
+++ b/usr.sbin/traceroute6/traceroute6.c
@@ -0,0 +1,1127 @@
+/*
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+/*-
+ * 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
+static char copyright[] =
+"@(#) Copyright (c) 1990, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)traceroute.c 8.1 (Berkeley) 6/6/93";
+#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/uio.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+
+#include <netinet/in.h>
+
+#include <arpa/inet.h>
+
+#include <netdb.h>
+#include <stdio.h>
+#include <err.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
+#include <netinet/udp.h>
+
+#ifdef IPSEC
+#include <net/route.h>
+#include <netinet6/ipsec.h>
+#endif
+
+#define freehostent(hp)
+#define DUMMY_PORT 10010
+
+#define MAXPACKET 65535 /* max ip packet size */
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 64
+#endif
+
+#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) bzero((char *)(p), sizeof(*(p)))
+#endif
+
+#define Fprintf (void)fprintf
+#define Sprintf (void)sprintf
+#define Printf (void)printf
+
+/*
+ * format of a (udp) probe packet.
+ */
+struct opacket {
+ u_char seq; /* sequence number of this packet */
+ u_char hops; /* hop limit of the packet */
+ struct timeval tv; /* time packet left */
+};
+
+u_char packet[512]; /* last inbound (icmp) packet */
+struct opacket *outpacket; /* last output (udp) packet */
+
+int main __P((int, char *[]));
+int wait_for_reply __P((int, struct msghdr *));
+int setpolicy __P((int so, char *policy));
+void send_probe __P((int, int));
+struct udphdr *get_udphdr __P((struct ip6_hdr *, u_char *));
+int get_hoplim __P((struct msghdr *));
+double deltaT __P((struct timeval *, struct timeval *));
+char *pr_type __P((int));
+int packet_ok __P((struct msghdr *, int, int));
+void print __P((struct msghdr *, int));
+void tvsub __P((struct timeval *, struct timeval *));
+char *inetname __P((struct in6_addr *));
+void usage __P((void));
+
+int rcvsock; /* receive (icmp) socket file descriptor */
+int sndsock; /* send (udp) socket file descriptor */
+struct timezone tz; /* leftover */
+
+struct msghdr rcvmhdr;
+struct iovec rcviov[2];
+int rcvhlim;
+struct in6_pktinfo *rcvpktinfo;
+
+struct sockaddr_in6 Src, Dst, Rcv;
+struct sockaddr_in6 *src = &Src, *dst = &Dst, *rcv = &Rcv;
+int datalen; /* How much data */
+char rtbuf[1024]; /*XXX*/
+struct cmsghdr *cmsg;
+
+char *source = 0;
+char *hostname;
+
+int nprobes = 3;
+int max_hops = 30;
+u_short 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 */
+int lflag; /* print both numerical address & hostname */
+
+char ntop_buf[INET6_ADDRSTRLEN]; /* for inet_ntop() */
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ extern char *optarg;
+ extern int optind;
+ struct hostent *hp;
+ int ch, i, on, probe, seq, hops;
+ static u_char rcvcmsgbuf[CMSG_SPACE(sizeof(struct in6_pktinfo))
+ + CMSG_SPACE(sizeof(int))];
+
+ on = 1;
+ seq = 0;
+
+ while ((ch = getopt(argc, argv, "dlm:np:q:rs:w:vg:")) != EOF)
+ switch(ch) {
+ case 'd':
+ options |= SO_DEBUG;
+ break;
+ case 'l':
+ lflag++;
+ break;
+ case 'g':
+ hp = getipnodebyname(optarg, AF_INET6, 0, &h_errno);
+ if (hp == NULL) {
+ Fprintf(stderr,
+ "traceroute6: unknown host %s\n", optarg);
+ exit(1);
+ }
+ if (cmsg == NULL)
+ cmsg = inet6_rthdr_init(rtbuf, IPV6_RTHDR_TYPE_0);
+ inet6_rthdr_add(cmsg, (struct in6_addr *)hp->h_addr, IPV6_RTHDR_LOOSE);
+ break;
+ case 'm':
+ max_hops = atoi(optarg);
+ if (max_hops <= 1) {
+ Fprintf(stderr,
+ "traceroute6: max hoplimit must be >1.\n");
+ exit(1);
+ }
+ break;
+ case 'n':
+ nflag++;
+ break;
+ case 'p':
+ port = atoi(optarg);
+ if (port < 1) {
+ Fprintf(stderr,
+ "traceroute6: port must be >0.\n");
+ exit(1);
+ }
+ break;
+ case 'q':
+ nprobes = atoi(optarg);
+ if (nprobes < 1) {
+ Fprintf(stderr,
+ "traceroute6: 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 'v':
+ verbose++;
+ break;
+ case 'w':
+ waittime = atoi(optarg);
+ if (waittime <= 1) {
+ Fprintf(stderr,
+ "traceroute6: wait must be >1 sec.\n");
+ exit(1);
+ }
+ break;
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1)
+ usage();
+
+#if 1
+ setvbuf(stdout, NULL, _IOLBF, BUFSIZ);
+#else
+ setlinebuf (stdout);
+#endif
+
+ (void) bzero((char *)dst, sizeof(Dst));
+ Dst.sin6_family = AF_INET6;
+
+ hp = (struct hostent *)gethostbyname2(*argv, AF_INET6);
+ if (hp == NULL) {
+ if (inet_pton(AF_INET6, *argv, &Dst.sin6_addr) != 1) {
+ (void)fprintf(stderr,
+ "traceroute6: unknown host %s\n", *argv);
+ exit(1);
+ }
+ hostname = *argv;
+ } else {
+ bcopy(hp->h_addr, (caddr_t)&Dst.sin6_addr, hp->h_length);
+ hostname = strdup(hp->h_name);
+ }
+ freehostent(hp);
+
+ if (*++argv)
+ datalen = atoi(*argv);
+ if (datalen < 0 || datalen >= MAXPACKET - sizeof(struct opacket)) {
+ Fprintf(stderr,
+ "traceroute6: packet size must be 0 <= s < %ld.\n",
+ (long)(MAXPACKET - sizeof(struct opacket)));
+ exit(1);
+ }
+ datalen += sizeof(struct opacket);
+ outpacket = (struct opacket *)malloc((unsigned)datalen);
+ if (! outpacket) {
+ perror("traceroute6: malloc");
+ exit(1);
+ }
+ (void) bzero((char *)outpacket, datalen);
+
+ /*
+ * Receive ICMP
+ */
+ if ((rcvsock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) {
+ perror("traceroute6: icmp socket");
+ exit(5);
+ }
+ /* initialize msghdr for receiving packets */
+ rcviov[0].iov_base = (caddr_t)packet;
+ rcviov[0].iov_len = sizeof(packet);
+ rcvmhdr.msg_name = (caddr_t)rcv;
+ rcvmhdr.msg_namelen = sizeof(*rcv);
+ rcvmhdr.msg_iov = rcviov;
+ rcvmhdr.msg_iovlen = 1;
+ rcvmhdr.msg_control = (caddr_t) rcvcmsgbuf;
+ rcvmhdr.msg_controllen = sizeof(rcvcmsgbuf);
+
+ /* specify to tell receiving interface */
+ if (setsockopt(rcvsock, IPPROTO_IPV6, IPV6_PKTINFO, &on,
+ sizeof(on)) < 0)
+ err(1, "setsockopt(IPV6_PKTINFO)");
+
+ /* specify to tell value of hoplimit field of received IP6 hdr */
+ if (setsockopt(rcvsock, IPPROTO_IPV6, IPV6_HOPLIMIT, &on,
+ sizeof(on)) < 0)
+ err(1, "setsockopt(IPV6_HOPLIMIT)");
+
+ if (options & SO_DEBUG)
+ (void) setsockopt(rcvsock, SOL_SOCKET, SO_DEBUG,
+ (char *)&on, sizeof(on));
+ if (options & SO_DONTROUTE)
+ (void) setsockopt(rcvsock, SOL_SOCKET, SO_DONTROUTE,
+ (char *)&on, sizeof(on));
+#ifdef IPSEC
+#ifdef IPSEC_POLICY_IPSEC
+ /*
+ * do not raise error even if setsockopt fails, kernel may have ipsec
+ * turned off.
+ */
+ if (setpolicy(rcvsock, "in bypass") < 0)
+ errx(1, ipsec_strerror());
+ if (setpolicy(rcvsock, "out bypass") < 0)
+ errx(1, ipsec_strerror());
+#else
+ {
+ int level = IPSEC_LEVEL_NONE;
+
+ (void)setsockopt(rcvsock, IPPROTO_IPV6, IPV6_ESP_TRANS_LEVEL, &level,
+ sizeof(level));
+ (void)setsockopt(rcvsock, IPPROTO_IPV6, IPV6_ESP_NETWORK_LEVEL, &level,
+ sizeof(level));
+#ifdef IP_AUTH_TRANS_LEVEL
+ (void)setsockopt(rcvsock, IPPROTO_IPV6, IPV6_AUTH_TRANS_LEVEL, &level,
+ sizeof(level));
+#else
+ (void)setsockopt(rcvsock, IPPROTO_IPV6, IPV6_AUTH_LEVEL, &level,
+ sizeof(level));
+#endif
+#ifdef IP_AUTH_NETWORK_LEVEL
+ (void)setsockopt(rcvsock, IPPROTO_IPV6, IPV6_AUTH_NETWORK_LEVEL, &level,
+ sizeof(level));
+#endif
+ }
+#endif /*IPSEC_POLICY_IPSEC*/
+#endif /*IPSEC*/
+
+ /*
+ * Send UDP
+ */
+ if ((sndsock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ perror("traceroute6: udp socket");
+ exit(5);
+ }
+#ifdef SO_SNDBUF
+ if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, (char *)&datalen,
+ sizeof(datalen)) < 0) {
+ perror("traceroute6: 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 (cmsg != NULL) {
+ inet6_rthdr_lasthop(cmsg, IPV6_RTHDR_LOOSE);
+ (void) setsockopt(sndsock, IPPROTO_IPV6, IPV6_PKTOPTIONS,
+ rtbuf, cmsg->cmsg_len);
+ }
+#ifdef IPSEC
+#ifdef IPSEC_POLICY_IPSEC
+ /*
+ * do not raise error even if setsockopt fails, kernel may have ipsec
+ * turned off.
+ */
+ if (setpolicy(sndsock, "in bypass") < 0)
+ errx(1, ipsec_strerror());
+ if (setpolicy(sndsock, "out bypass") < 0)
+ errx(1, ipsec_strerror());
+#else
+ {
+ int level = IPSEC_LEVEL_BYPASS;
+
+ (void)setsockopt(sndsock, IPPROTO_IPV6, IPV6_ESP_TRANS_LEVEL, &level,
+ sizeof(level));
+ (void)setsockopt(sndsock, IPPROTO_IPV6, IPV6_ESP_NETWORK_LEVEL, &level,
+ sizeof(level));
+#ifdef IP_AUTH_TRANS_LEVEL
+ (void)setsockopt(sndsock, IPPROTO_IPV6, IPV6_AUTH_TRANS_LEVEL, &level,
+ sizeof(level));
+#else
+ (void)setsockopt(sndsock, IPPROTO_IPV6, IPV6_AUTH_LEVEL, &level,
+ sizeof(level));
+#endif
+#ifdef IP_AUTH_NETWORK_LEVEL
+ (void)setsockopt(sndsock, IPPROTO_IPV6, IPV6_AUTH_NETWORK_LEVEL, &level,
+ sizeof(level));
+#endif
+ }
+#endif /*IPSEC_POLICY_IPSEC*/
+#endif /*IPSEC*/
+
+ /*
+ * Source selection
+ */
+ bzero((char *)src, sizeof(Src));
+ if (source) {
+ if (inet_pton(AF_INET6, source, &Src.sin6_addr) != 1) {
+ Printf("traceroute6: unknown host %s\n", source);
+ exit(1);
+ }
+ } else {
+ struct sockaddr_in6 Nxt;
+ int dummy, len;
+
+ len = sizeof(Src);
+ Nxt = Dst;
+ Nxt.sin6_port = htons(DUMMY_PORT);
+ if (cmsg != NULL)
+ bcopy(inet6_rthdr_getaddr(cmsg, 1), &Nxt.sin6_addr,
+ sizeof(Nxt.sin6_addr));
+ if ((dummy = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ perror("socket") ;
+ }
+ if(-1 == connect(dummy, (struct sockaddr *)&Nxt, sizeof(Nxt)))
+ perror("connect");
+ if(-1 == getsockname(dummy, (struct sockaddr *)src, &len)) {
+ perror("getsockname");
+ printf("%d\n", errno);
+ }
+ close(dummy) ;
+ }
+ ident = (getpid() & 0xffff) | 0x8000;
+ Src.sin6_family = AF_INET6;
+ Src.sin6_port = htons(ident);
+ if (bind(sndsock, (struct sockaddr *)src, sizeof(Src)) < 0){
+ perror ("traceroute6: bind:");
+ exit (1);
+ }
+
+ /*
+ * Message to users
+ */
+ Fprintf(stderr, "traceroute to %s (%s)", hostname,
+ inet_ntop(AF_INET6, &Dst.sin6_addr,
+ ntop_buf, sizeof(ntop_buf)));
+ if (source)
+ Fprintf(stderr, " from %s", source);
+ Fprintf(stderr, ", %d hops max, %d byte packets\n", max_hops, datalen);
+ (void) fflush(stderr);
+
+ /*
+ * Main loop
+ */
+ for (hops = 1; hops <= max_hops; ++hops) {
+ struct in6_addr lastaddr;
+ int got_there = 0;
+ int unreachable = 0;
+
+ Printf("%2d ", hops);
+ bzero(&lastaddr, sizeof(lastaddr));
+ for (probe = 0; probe < nprobes; ++probe) {
+ int cc;
+ struct timeval t1, t2;
+ struct timezone tz;
+
+ (void) gettimeofday(&t1, &tz);
+ send_probe(++seq, hops);
+ while ((cc = wait_for_reply(rcvsock, &rcvmhdr))) {
+ (void) gettimeofday(&t2, &tz);
+ if ((i = packet_ok(&rcvmhdr, cc, seq))) {
+ if (! IN6_ARE_ADDR_EQUAL(&Rcv.sin6_addr,
+ &lastaddr)) {
+ print(&rcvmhdr, cc);
+ lastaddr = Rcv.sin6_addr;
+ }
+ Printf(" %g ms", deltaT(&t1, &t2));
+ switch(i - 1) {
+ case ICMP6_DST_UNREACH_NOROUTE:
+ ++unreachable;
+ Printf(" !N");
+ break;
+ case ICMP6_DST_UNREACH_ADMIN:
+ ++unreachable;
+ Printf(" !P");
+ break;
+ case ICMP6_DST_UNREACH_NOTNEIGHBOR:
+ ++unreachable;
+ Printf(" !S");
+ break;
+ case ICMP6_DST_UNREACH_ADDR:
+ ++unreachable;
+ Printf(" !A");
+ break;
+ case ICMP6_DST_UNREACH_NOPORT:
+ if (rcvhlim >= 0 &&
+ rcvhlim <= 1)
+ Printf(" !");
+ ++got_there;
+ break;
+ }
+ break;
+ }
+ }
+ if (cc == 0)
+ Printf(" *");
+ (void) fflush(stdout);
+ }
+ putchar('\n');
+ if (got_there ||
+ (unreachable > 0 && unreachable >= ((nprobes + 1) / 2))) {
+ exit(0);
+ }
+ }
+
+ exit(0);
+}
+
+int
+wait_for_reply(sock, mhdr)
+ int sock;
+ struct msghdr *mhdr;
+{
+ fd_set fds;
+ struct timeval wait;
+ int cc = 0;
+
+ FD_ZERO(&fds);
+ FD_SET(sock, &fds);
+ wait.tv_sec = waittime; wait.tv_usec = 0;
+
+ if (select(sock+1, &fds, (fd_set *)0, (fd_set *)0, &wait) > 0)
+ cc = recvmsg(rcvsock, mhdr, 0);
+
+ return(cc);
+}
+
+#ifdef IPSEC
+#ifdef IPSEC_POLICY_IPSEC
+int
+setpolicy(so, policy)
+ int so;
+ char *policy;
+{
+ char *buf;
+
+ buf = ipsec_set_policy(policy, strlen(policy));
+ if (buf == NULL) {
+ warnx(ipsec_strerror());
+ return -1;
+ }
+ (void)setsockopt(so, IPPROTO_IPV6, IPV6_IPSEC_POLICY,
+ buf, ipsec_get_policylen(buf));
+
+ free(buf);
+
+ return 0;
+}
+#endif
+#endif
+
+void
+send_probe(seq, hops)
+ int seq, hops;
+{
+ struct opacket *op = outpacket;
+ int i;
+
+ if(setsockopt(sndsock, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
+ (char *)&hops, sizeof(hops)) < 0) {
+ perror("setsockopt IPV6_UNICAST_HOPS");
+ }
+
+ Dst.sin6_port = htons(port + seq);
+
+ op->seq = seq;
+ op->hops = hops;
+ (void) gettimeofday(&op->tv, &tz);
+
+ i = sendto(sndsock, (char *)outpacket, datalen , 0,
+ (struct sockaddr *)dst, sizeof(Dst));
+ if (i < 0 || i != datalen) {
+ if (i<0)
+ perror("sendto");
+ Printf("traceroute6: wrote %s %d chars, ret=%d\n", hostname,
+ datalen, i);
+ (void) fflush(stdout);
+ }
+}
+
+int
+get_hoplim(mhdr)
+ struct msghdr *mhdr;
+{
+ struct cmsghdr *cm;
+
+ for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(mhdr); cm;
+ cm = (struct cmsghdr *)CMSG_NXTHDR(mhdr, cm)) {
+ if (cm->cmsg_level == IPPROTO_IPV6 &&
+ cm->cmsg_type == IPV6_HOPLIMIT &&
+ cm->cmsg_len == CMSG_LEN(sizeof(int)))
+ return(*(int *)CMSG_DATA(cm));
+ }
+
+ return(-1);
+}
+
+double
+deltaT(t1p, t2p)
+ struct timeval *t1p, *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(t0)
+ int t0;
+{
+ u_char t = t0 & 0xff;
+ char *cp;
+
+ switch (t) {
+ case ICMP6_DST_UNREACH:
+ cp = "Destination Unreachable";
+ break;
+ case ICMP6_PACKET_TOO_BIG:
+ cp = "Pakcet Too Big";
+ break;
+ case ICMP6_TIME_EXCEEDED:
+ cp = "Time Exceeded";
+ break;
+ case ICMP6_PARAM_PROB:
+ cp = "Parameter Problem";
+ break;
+ case ICMP6_ECHO_REQUEST:
+ cp = "Echo Request";
+ break;
+ case ICMP6_ECHO_REPLY:
+ cp = "Echo Reply";
+ break;
+ case ICMP6_MEMBERSHIP_QUERY:
+ cp = "Group Membership Query";
+ break;
+ case ICMP6_MEMBERSHIP_REPORT:
+ cp = "Group Membership Report";
+ break;
+ case ICMP6_MEMBERSHIP_REDUCTION:
+ cp = "Group Membership Reduction";
+ break;
+ case ND_ROUTER_SOLICIT:
+ cp = "Router Solicitation";
+ break;
+ case ND_ROUTER_ADVERT:
+ cp = "Router Advertisement";
+ break;
+ case ND_NEIGHBOR_SOLICIT:
+ cp = "Neighbor Solicitation";
+ break;
+ case ND_NEIGHBOR_ADVERT:
+ cp = "Neighbor Advertisement";
+ break;
+ case ND_REDIRECT:
+ cp = "Ridirect";
+ break;
+ default:
+ cp = "Unknown";
+ break;
+ }
+ return cp;
+}
+
+
+int
+packet_ok(mhdr, cc, seq)
+ struct msghdr *mhdr;
+ int cc;
+ int seq;
+{
+ register struct icmp6_hdr *icp;
+ struct sockaddr_in6 *from = (struct sockaddr_in6 *)mhdr->msg_name;
+ u_char type, code;
+ char *buf = (char *)mhdr->msg_iov[0].iov_base;
+ struct cmsghdr *cm;
+ int *hlimp;
+
+#ifdef OLDRAWSOCKET
+ int hlen;
+ struct ip6_hdr *ip;
+#endif
+
+#ifdef OLDRAWSOCKET
+ ip = (struct ip6_hdr *) buf;
+ hlen = sizeof(struct ip6_hdr);
+ if (cc < hlen + sizeof(struct icmp6_hdr)) {
+ if (verbose)
+ Printf("packet too short (%d bytes) from %s\n", cc,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntop_buf, sizeof(ntop_buf)));
+ return (0);
+ }
+ cc -= hlen;
+ icp = (struct icmp6_hdr *)(buf + hlen);
+#else
+ if (cc < sizeof(struct icmp6_hdr)) {
+ if (verbose)
+ Printf("data too short (%d bytes) from %s\n", cc,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntop_buf, sizeof(ntop_buf)));
+ return(0);
+ }
+ icp = (struct icmp6_hdr *)buf;
+#endif
+ /* get optional information via advanced API */
+ rcvpktinfo = NULL;
+ hlimp = NULL;
+ for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(mhdr); cm;
+ cm = (struct cmsghdr *)CMSG_NXTHDR(mhdr, cm)) {
+ if (cm->cmsg_level == IPPROTO_IPV6 &&
+ cm->cmsg_type == IPV6_PKTINFO &&
+ cm->cmsg_len ==
+ CMSG_LEN(sizeof(struct in6_pktinfo)))
+ rcvpktinfo = (struct in6_pktinfo *)(CMSG_DATA(cm));
+
+ if (cm->cmsg_level == IPPROTO_IPV6 &&
+ cm->cmsg_type == IPV6_HOPLIMIT &&
+ cm->cmsg_len == CMSG_LEN(sizeof(int)))
+ hlimp = (int *)CMSG_DATA(cm);
+ }
+ if (rcvpktinfo == NULL || hlimp == NULL) {
+ warnx("failed to get received hop limit or packet info");
+ return(0);
+ }
+ rcvhlim = *hlimp;
+
+ 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;
+
+ hip = (struct ip6_hdr *)(icp + 1);
+ if ((up = get_udphdr(hip, (u_char *)(buf + cc))) == NULL) {
+ if (verbose)
+ warnx("failed to get upper layer header");
+ return(0);
+ }
+ if (up->uh_sport == htons(ident) &&
+ up->uh_dport == htons(port+seq))
+ return (type == ICMP6_TIME_EXCEEDED ? -1 : code + 1);
+ }
+ if (verbose) {
+ int i;
+ u_long *lp = (u_long *)(icp + 1);
+ char sbuf[INET6_ADDRSTRLEN+1], dbuf[INET6_ADDRSTRLEN];
+
+ Printf("\n%d bytes from %s to %s", cc,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ sbuf, sizeof(sbuf)),
+ inet_ntop(AF_INET6, &rcvpktinfo->ipi6_addr,
+ dbuf, sizeof(dbuf)));
+ Printf(": icmp type %d (%s) code %d\n", type, pr_type(type),
+ icp->icmp6_code);
+ for (i = 4; i < cc ; i += sizeof(long))
+ Printf("%2d: %8.8x\n", i, (u_int32_t)ntohl(*lp++));
+ }
+ return(0);
+}
+
+/*
+ * Increment pointer until find the UDP header.
+ */
+struct udphdr *
+get_udphdr(ip6, lim)
+ struct ip6_hdr *ip6;
+ u_char *lim;
+{
+ u_char *cp = (u_char *)ip6, nh;
+ int hlen;
+
+ if (cp + sizeof(*ip6) >= lim)
+ return(NULL);
+
+ nh = ip6->ip6_nxt;
+ cp += sizeof(struct ip6_hdr);
+
+ while(lim - cp >= 8) {
+ switch(nh) {
+ case IPPROTO_ESP:
+ case IPPROTO_TCP:
+ case IPPROTO_ICMPV6:
+ return(NULL);
+ case IPPROTO_UDP:
+ return((struct udphdr *)cp);
+ case IPPROTO_FRAGMENT:
+ hlen = sizeof(struct ip6_frag);
+ nh = ((struct ip6_frag *)cp)->ip6f_nxt;
+ break;
+ case IPPROTO_AH:
+ hlen = (((struct ip6_ext *)cp)->ip6e_len + 2) << 2;
+ nh = ((struct ip6_ext *)cp)->ip6e_nxt;
+ break;
+ default:
+ hlen = (((struct ip6_ext *)cp)->ip6e_len + 1) << 3;
+ nh = ((struct ip6_ext *)cp)->ip6e_nxt;
+ break;
+ }
+
+ cp += hlen;
+ }
+
+ return(NULL);
+}
+
+void
+print(mhdr, cc)
+ struct msghdr *mhdr;
+ int cc;
+{
+ struct sockaddr_in6 *from = (struct sockaddr_in6 *)mhdr->msg_name;
+
+ if (nflag) {
+ Printf(" %s", inet_ntop(AF_INET6, &from->sin6_addr,
+ ntop_buf, sizeof(ntop_buf)));
+ }
+ else if (lflag) {
+ Printf(" %s (%s)", inetname(&from->sin6_addr),
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntop_buf, sizeof(ntop_buf)));
+ }
+ else {
+ Printf(" %s", inetname(&from->sin6_addr));
+ }
+
+ if (verbose) {
+#ifdef OLDRAWSOCKET
+ Printf(" %d bytes to %s", cc,
+ inet_ntop(AF_INET6, &rcvpktinfo->ipi6_addr,
+ ntop_buf, sizeof(ntop_buf)));
+#else
+ Printf(" %d bytes of data to %s", cc,
+ inet_ntop(AF_INET6, &rcvpktinfo->ipi6_addr,
+ ntop_buf, sizeof(ntop_buf)));
+#endif
+ }
+}
+
+/*
+ * 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;
+}
+
+
+/*
+ * Construct an Internet address representation.
+ * If the nflag has been supplied, give
+ * numeric value, otherwise try for symbolic name.
+ */
+char *
+inetname(in)
+ struct in6_addr *in;
+{
+ register char *cp;
+ static char line[50];
+ struct hostent *hp;
+ static char domain[MAXHOSTNAMELEN + 1];
+ static int first = 1;
+
+ if (first && !nflag) {
+ first = 0;
+ if (gethostname(domain, MAXHOSTNAMELEN) == 0 &&
+ (cp = index(domain, '.')))
+ (void) strcpy(domain, cp + 1);
+ else
+ domain[0] = 0;
+ }
+ cp = 0;
+ if (!nflag) {
+ /* hp = (struct hostent *)addr2hostname(in, sizeof(*in), AF_INET6, &herr); */
+ hp = (struct hostent *)gethostbyaddr((const char *)in, sizeof(*in), AF_INET6);
+ if (hp) {
+ if ((cp = index(hp->h_name, '.')) &&
+ !strcmp(cp + 1, domain))
+ *cp = 0;
+ cp = hp->h_name;
+ freehostent(hp);
+ }
+ }
+ if (cp)
+ (void) strcpy(line, cp);
+ else {
+ (void)inet_ntop(AF_INET6, in, line, sizeof(line));
+ }
+ return (line);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr,
+"usage: traceroute6 [-dlnrv] [-m max_hops] [-p port#] [-q nqueries]\n\t\
+[-s src_addr] [-g gateway] [-w wait] host [data size]\n");
+ exit(1);
+}
OpenPOWER on IntegriCloud