diff --git a/include/netdb.h b/include/netdb.h index 987523ebd6fe..4206c731dccf 100644 --- a/include/netdb.h +++ b/include/netdb.h @@ -62,6 +62,7 @@ #define _NETDB_H_ #include +#include #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 e44e43abc069..4a0cf7470aba 100644 --- a/include/resolv.h +++ b/include/resolv.h @@ -60,6 +60,7 @@ #include #include #include +#include #include /* @@ -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 5b7dc8535b92..87ac304c2a1f 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 000000000000..39bcdb710249 --- /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 +.Fd #include +.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 +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 , +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 000000000000..79b07dee28b3 --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + */ + 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 000000000000..7220af4094fc --- /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 +.Fd #include +.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 000000000000..c110ac92a523 --- /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 +.Fd #include +.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 : +.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 +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 . +.\" +.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 000000000000..76edc12378d0 --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 000000000000..668345149c9e --- /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 + */ + +/* + * TODO for thread safe + * use mutex for _hostconf, _hostconf_init. + * rewrite resolvers to be thread safe + */ + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#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 +#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 +#include +#include + +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 8f6e649703cf..d1e624928993 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 178dd9cd15ae..bedcd449af74 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 000000000000..0cac4c084490 --- /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 diff --git a/sbin/ping6/ping6.8 b/sbin/ping6/ping6.8 new file mode 100644 index 000000000000..76509fac2d7f --- /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 000000000000..d1186559400c --- /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 +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef IPSEC +#include +#include +#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 000000000000..d373d6cbcd0c --- /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 diff --git a/sys/netinet6/in6_prefix.c b/sys/netinet6/in6_prefix.c index 6799d0d3247c..f55e30da847d 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 ad7d8243621e..792797ca829d 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 diff --git a/usr.bin/netstat/inet6.c b/usr.bin/netstat/inet6.c index 154ee32d8ae1..02c2fbf9a58e 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 5bf9759cba52..c0e4381c3dea 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 1dce61556a45..69fb8e8d6c8a 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 000000000000..b562390e8d90 --- /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 diff --git a/usr.sbin/gifconfig/gifconfig.8 b/usr.sbin/gifconfig/gifconfig.8 new file mode 100644 index 000000000000..702777f7d28e --- /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 000000000000..3f36b03800c4 --- /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 +#include +#include +#include + +#include +#if defined(__FreeBSD__) && __FreeBSD__ >= 3 +#include +#endif /* __FreeBSD__ >= 3 */ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 000000000000..1cebcca5ac30 --- /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 diff --git a/usr.sbin/ifmcstat/ifmcstat.8 b/usr.sbin/ifmcstat/ifmcstat.8 new file mode 100644 index 000000000000..7f9d047f7aff --- /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 000000000000..3167b960ca8b --- /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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#if defined(__FreeBSD__) && __FreeBSD__ >= 3 +# include +#endif +#include +#include +#include +#ifndef __NetBSD__ +# ifdef __FreeBSD__ +# define KERNEL +# endif +# include +# ifdef __FreeBSD__ +# undef KERNEL +# endif +#else +# include +#endif +#include +#include + +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 000000000000..323d6d3e1478 --- /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 diff --git a/usr.sbin/prefix/prefix.8 b/usr.sbin/prefix/prefix.8 new file mode 100644 index 000000000000..e3a35d0bd6bd --- /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 000000000000..53c954690a23 --- /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 +#include +#include +#include +#include + +#include +#if defined(__FreeBSD__) && __FreeBSD__ >= 3 +#include +#endif /* __FreeBSD__ >= 3 */ +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 000000000000..8a0cd40147a3 --- /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 diff --git a/usr.sbin/rip6query/rip6query.8 b/usr.sbin/rip6query/rip6query.8 new file mode 100644 index 000000000000..bd64068d3575 --- /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 000000000000..59e7932a57a3 --- /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 + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#if defined(__FreeBSD__) && __FreeBSD__ >= 3 +#include +#endif /* __FreeBSD__ >= 3 */ +#include +#include +#include +#include + +#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 000000000000..ed5354ee18c4 --- /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 diff --git a/usr.sbin/route6d/misc/chkrt b/usr.sbin/route6d/misc/chkrt new file mode 100755 index 000000000000..f57a376d378c --- /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"; +$_ = ; +chop; +close(FD); +system("kill -INT $_"); + +open(NS, "/usr/local/v6/bin/netstat -r -n|") || die "Can not open netstat"; +while () { + 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 () { + 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 000000000000..bff7e2d5f7f4 --- /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 + +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 000000000000..5ccd611e1e68 --- /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 000000000000..857f5cb232a5 --- /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 + +#include +#include +#include +#include +#include +#ifdef __STDC__ +#include +#else +#include +#endif +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#ifdef ADVAPI +#include +#endif +#include +#if defined(__FreeBSD__) && __FreeBSD__ >= 3 +#include +#endif /* __FreeBSD__ >= 3 */ +#define KERNEL 1 +#include +#undef KERNEL +#include +#include +#include +#include +#include + +#include + +#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 000000000000..7808c04fbdb8 --- /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 000000000000..e348b27d06ec --- /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 diff --git a/usr.sbin/rtsold/dump.c b/usr.sbin/rtsold/dump.c new file mode 100644 index 000000000000..15877210069c --- /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 +#include + +#include +#include + +#include +#include +#include +#include +#include + +#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 000000000000..4ac1d4bca709 --- /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 +#include +#include +#include + +#include +#if defined(__FreeBSD__) && __FreeBSD__ >= 3 +#include +#endif /* __FreeBSD__ >= 3 */ +#include +#include +#include +#include +#ifdef __FreeBSD__ +# include +#endif +#ifdef __NetBSD__ +#include +#endif +#if defined(__bsdi__) || defined(__OpenBSD__) +# include +# include +#endif +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 000000000000..4b42c215d1ed --- /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 +#include +#include +#include +#include + +#include +#if defined(__FreeBSD__) && __FreeBSD__ >= 3 +#include +#endif /* __FreeBSD__ >= 3 */ + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#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 000000000000..5b76403da695 --- /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 +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#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 000000000000..90d483ad6a7e --- /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 000000000000..8d5646f54a93 --- /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 +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 000000000000..afb5088967be --- /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 000000000000..2b151a68faf9 --- /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 diff --git a/usr.sbin/traceroute6/traceroute6.8 b/usr.sbin/traceroute6/traceroute6.8 new file mode 100644 index 000000000000..b98773f39164 --- /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 000000000000..a1b665e47563 --- /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 +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef IPSEC +#include +#include +#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); +}