summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--contrib/telnet/telnet/commands.c25
-rw-r--r--lib/libc/net/Makefile.inc16
-rw-r--r--lib/libc/net/getaddrinfo.c430
-rw-r--r--lib/libc/net/inet6_opt_init.3291
-rw-r--r--lib/libc/net/inet6_rth_space.3254
-rw-r--r--lib/libc/net/ip6opt.c227
-rw-r--r--lib/libc/net/rthdr.c548
-rw-r--r--lib/libsdp/search.c1
-rw-r--r--sbin/ping6/Makefile6
-rw-r--r--sbin/ping6/ping6.8132
-rw-r--r--sbin/ping6/ping6.c381
-rw-r--r--sys/netinet/icmp6.h51
-rw-r--r--sys/netinet/in.h1
-rw-r--r--sys/netinet/in_pcb.h10
-rw-r--r--sys/netinet/ip6.h21
-rw-r--r--sys/netinet6/icmp6.c41
-rw-r--r--sys/netinet6/in6.h123
-rw-r--r--sys/netinet6/in6_pcb.c3
-rw-r--r--sys/netinet6/in6_var.h5
-rw-r--r--sys/netinet6/ip6_input.c89
-rw-r--r--sys/netinet6/ip6_output.c1235
-rw-r--r--sys/netinet6/ip6_var.h41
-rw-r--r--sys/netinet6/mld6.c2
-rw-r--r--sys/netinet6/nd6.c65
-rw-r--r--sys/netinet6/nd6.h37
-rw-r--r--sys/netinet6/nd6_rtr.c3
-rw-r--r--sys/netinet6/raw_ip6.c48
-rw-r--r--sys/netinet6/route6.c3
-rw-r--r--sys/netinet6/udp6_output.c5
-rw-r--r--usr.sbin/mld6query/Makefile2
-rw-r--r--usr.sbin/mld6query/mld6.c113
-rw-r--r--usr.sbin/traceroute6/Makefile2
32 files changed, 3087 insertions, 1124 deletions
diff --git a/contrib/telnet/telnet/commands.c b/contrib/telnet/telnet/commands.c
index 2635a20..906a875 100644
--- a/contrib/telnet/telnet/commands.c
+++ b/contrib/telnet/telnet/commands.c
@@ -2846,7 +2846,7 @@ sourceroute(struct addrinfo *ai, char *arg, char **cpp, int *lenp, int *protop,
struct sockaddr_in *_sin;
#ifdef INET6
struct sockaddr_in6 *sin6;
- struct cmsghdr *cmsg;
+ struct ip6_rthdr *rth;
#endif
struct addrinfo hints, *res;
int error;
@@ -2889,11 +2889,13 @@ sourceroute(struct addrinfo *ai, char *arg, char **cpp, int *lenp, int *protop,
#ifdef INET6
if (ai->ai_family == AF_INET6) {
- cmsg = inet6_rthdr_init(*cpp, IPV6_RTHDR_TYPE_0);
+ if ((rth = inet6_rth_init((void *)*cpp, sizeof(buf),
+ IPV6_RTHDR_TYPE_0, 0)) == NULL)
+ return -1;
if (*cp != '@')
return -1;
*protop = IPPROTO_IPV6;
- *optp = IPV6_PKTOPTIONS;
+ *optp = IPV6_RTHDR;
} else
#endif
{
@@ -2965,8 +2967,8 @@ sourceroute(struct addrinfo *ai, char *arg, char **cpp, int *lenp, int *protop,
#ifdef INET6
if (res->ai_family == AF_INET6) {
sin6 = (struct sockaddr_in6 *)res->ai_addr;
- inet6_rthdr_add(cmsg, &sin6->sin6_addr,
- IPV6_RTHDR_LOOSE);
+ if (inet6_rth_add((void *)rth, &sin6->sin6_addr) == -1)
+ return(0);
} else
#endif
{
@@ -2981,23 +2983,14 @@ sourceroute(struct addrinfo *ai, char *arg, char **cpp, int *lenp, int *protop,
/*
* Check to make sure there is space for next address
*/
-#ifdef INET6
- if (res->ai_family == AF_INET6) {
- if (((char *)CMSG_DATA(cmsg) +
- sizeof(struct ip6_rthdr) +
- ((inet6_rthdr_segments(cmsg) + 1) *
- sizeof(struct in6_addr))) > ep)
- return -1;
- } else
-#endif
if (lsrp + 4 > ep)
return -1;
freeaddrinfo(res);
}
#ifdef INET6
if (res->ai_family == AF_INET6) {
- inet6_rthdr_lasthop(cmsg, IPV6_RTHDR_LOOSE);
- *lenp = cmsg->cmsg_len;
+ rth->ip6r_len = rth->ip6r_segleft * 2;
+ *lenp = (rth->ip6r_len + 1) << 3;
} else
#endif
{
diff --git a/lib/libc/net/Makefile.inc b/lib/libc/net/Makefile.inc
index 01dc3b61..a00269e 100644
--- a/lib/libc/net/Makefile.inc
+++ b/lib/libc/net/Makefile.inc
@@ -44,7 +44,8 @@ MAN+= addr2ascii.3 byteorder.3 ethers.3 getaddrinfo.3 gethostbyname.3 \
getnameinfo.3 getnetent.3 getprotoent.3 getservent.3 hesiod.3 \
if_indextoname.3 \
inet.3 inet_net.3 \
- inet6_option_space.3 inet6_rthdr_space.3 linkaddr.3 \
+ inet6_opt_init.3 inet6_option_space.3 inet6_rth_space.3 \
+ inet6_rthdr_space.3 linkaddr.3 \
nsdispatch.3 rcmd.3 rcmdsh.3 resolver.3 sockatmark.3
MLINKS+=addr2ascii.3 ascii2addr.3
@@ -73,11 +74,22 @@ MLINKS+=inet.3 addr.3 inet.3 inet_addr.3 inet.3 inet_aton.3 \
inet.3 inet_ntop.3 inet.3 inet_pton.3 \
inet.3 network.3 inet.3 ntoa.3
MLINKS+=inet_net.3 inet_net_ntop.3 inet_net.3 inet_net_pton.3
-MLINKS+=inet6_option_space.3 inet6_option_alloc.3 \
+MLINKS+=inet6_opt_init.3 inet6_opt_append.3 \
+ inet6_opt_init.3 inet6_opt_find.3 \
+ inet6_opt_init.3 inet6_opt_finish.3 \
+ inet6_opt_init.3 inet6_opt_get_val.3 \
+ inet6_opt_init.3 inet6_opt_next.3 \
+ inet6_opt_init.3 inet6_opt_set_val.3 \
+ inet6_option_space.3 inet6_option_alloc.3 \
inet6_option_space.3 inet6_option_append.3 \
inet6_option_space.3 inet6_option_find.3 \
inet6_option_space.3 inet6_option_init.3 \
inet6_option_space.3 inet6_option_next.3 \
+ inet6_rth_space.3 inet6_rth_add.3 \
+ inet6_rth_space.3 inet6_rth_getaddr.3 \
+ inet6_rth_space.3 inet6_rth_init.3 \
+ inet6_rth_space.3 inet6_rth_reverse.3 \
+ inet6_rth_space.3 inet6_rth_segments.3 \
inet6_rthdr_space.3 inet6_rthdr_add.3 \
inet6_rthdr_space.3 inet6_rthdr_getaddr.3 \
inet6_rthdr_space.3 inet6_rthdr_getflags.3 \
diff --git a/lib/libc/net/getaddrinfo.c b/lib/libc/net/getaddrinfo.c
index e8c6d4f..026909a 100644
--- a/lib/libc/net/getaddrinfo.c
+++ b/lib/libc/net/getaddrinfo.c
@@ -1,4 +1,4 @@
-/* $KAME: getaddrinfo.c,v 1.15 2000/07/09 04:37:24 itojun Exp $ */
+/* $KAME: getaddrinfo.c,v 1.160 2003/05/17 01:30:42 itojun Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -37,6 +37,10 @@
* - 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.
+ * - IPv4 classful (shortened) form. RFC2553 is silent about it. XNET 5.2
+ * says to use inet_aton() to convert IPv4 numeric to binary (allows
+ * classful form as a result).
+ * current code - disallow classful form for IPv4 (due to use of inet_pton).
* - freeaddrinfo(NULL). RFC2553 is silent about it. XNET 5.2 says it is
* invalid. current code - SEGV on freeaddrinfo(NULL)
*
@@ -71,11 +75,15 @@ __FBSDID("$FreeBSD$");
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
+#ifdef INET6
+#include <sys/queue.h>
+#include <net/if_var.h>
+#include <sys/sysctl.h>
+#include <sys/ioctl.h>
+#include <netinet6/in6_var.h> /* XXX */
+#endif /* INET6 */
#include <arpa/inet.h>
#include <arpa/nameser.h>
-#include <rpc/rpc.h>
-#include <rpcsvc/yp_prot.h>
-#include <rpcsvc/ypclnt.h>
#include <netdb.h>
#include <pthread.h>
#include <resolv.h>
@@ -153,23 +161,24 @@ struct explore {
#define WILD_AF(ex) ((ex)->e_wild & 0x01)
#define WILD_SOCKTYPE(ex) ((ex)->e_wild & 0x02)
#define WILD_PROTOCOL(ex) ((ex)->e_wild & 0x04)
+#define WILD_ACTIVE(ex) ((ex)->e_wild & 0x08)
+#define WILD_PASSIVE(ex) ((ex)->e_wild & 0x10)
};
static const struct explore explore[] = {
#if 0
- { PF_LOCAL, 0, ANY, ANY, NULL, 0x01 },
+ { PF_LOCAL, ANY, ANY, NULL, 0x01 },
#endif
#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 },
+ { PF_INET6, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x1f },
+ { PF_INET6, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x0f }, /* !PASSIVE */
+ { PF_INET6, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x17 }, /* PASSIVE */
+ { PF_INET6, SOCK_RAW, ANY, NULL, 0x1d },
#endif
- { PF_INET, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 },
- { PF_INET, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 },
- { PF_INET, SOCK_RAW, ANY, NULL, 0x05 },
- { PF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 },
- { PF_UNSPEC, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 },
- { PF_UNSPEC, SOCK_RAW, ANY, NULL, 0x05 },
+ { PF_INET, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x1f },
+ { PF_INET, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x0f }, /* !PASSIVE */
+ { PF_INET, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x17 }, /* PASSIVE */
+ { PF_INET, SOCK_RAW, ANY, NULL, 0x1d },
{ -1, 0, 0, NULL, 0 },
};
@@ -179,6 +188,8 @@ static const struct explore explore[] = {
#define PTON_MAX 4
#endif
+#define AIO_SRCFLAG_DEPRECATED 0x1
+
static const ns_src default_dns_files[] = {
{ NSSRC_FILES, NS_SUCCESS },
{ NSSRC_DNS, NS_SUCCESS },
@@ -202,20 +213,23 @@ typedef union {
} querybuf;
static int str_isnumber(const char *);
+static int explore_copy(const struct addrinfo *, const struct addrinfo *,
+ struct addrinfo **);
static int explore_null(const struct addrinfo *,
const char *, struct addrinfo **);
static int explore_numeric(const struct addrinfo *, const char *,
- const char *, struct addrinfo **);
+ const char *, struct addrinfo **, const char *);
static int explore_numeric_scope(const struct addrinfo *, const char *,
const char *, struct addrinfo **);
static int get_canonname(const struct addrinfo *,
struct addrinfo *, const char *);
static struct addrinfo *get_ai(const struct addrinfo *,
const struct afd *, const char *);
+static struct addrinfo *copy_ai(const struct addrinfo *);
static int get_portmatch(const struct addrinfo *, const char *);
static int get_port(struct addrinfo *, const char *, int);
static const struct afd *find_afd(int);
-static int addrconfig(struct addrinfo *);
+static int addrconfig(int);
#ifdef INET6
static int ip6_str2scopeid(char *, struct sockaddr_in6 *, u_int32_t *);
#endif
@@ -373,10 +387,20 @@ getaddrinfo(hostname, servname, hints, res)
struct addrinfo sentinel;
struct addrinfo *cur;
int error = 0;
- struct addrinfo ai;
- struct addrinfo ai0;
+ struct addrinfo ai, ai0, *afai;
struct addrinfo *pai;
+ const struct afd *afd;
const struct explore *ex;
+ struct addrinfo *afailist[sizeof(afdl)/sizeof(afdl[0])];
+ struct addrinfo *afai_unspec;
+ int found;
+ int numeric = 0;
+
+ /* ensure we return NULL on errors */
+ *res = NULL;
+
+ memset(afailist, 0, sizeof(afailist));
+ afai_unspec = NULL;
memset(&sentinel, 0, sizeof(sentinel));
cur = &sentinel;
@@ -417,20 +441,27 @@ getaddrinfo(hostname, servname, hints, res)
*/
if (pai->ai_socktype != ANY && pai->ai_protocol != ANY) {
for (ex = explore; ex->e_af >= 0; ex++) {
- if (pai->ai_family != ex->e_af)
+ if (!MATCH_FAMILY(pai->ai_family, ex->e_af,
+ WILD_AF(ex)))
continue;
- if (ex->e_socktype == ANY)
+ if (!MATCH(pai->ai_socktype, ex->e_socktype,
+ WILD_SOCKTYPE(ex)))
continue;
- if (ex->e_protocol == ANY)
+ if (!MATCH(pai->ai_protocol, ex->e_protocol,
+ WILD_PROTOCOL(ex)))
continue;
- if (pai->ai_socktype == ex->e_socktype &&
- pai->ai_protocol != ex->e_protocol) {
- ERR(EAI_BADHINTS);
- }
+
+ /* matched */
+ break;
+ }
+
+ if (ex->e_af < 0) {
+ ERR(EAI_BADHINTS);
}
}
}
+#if defined(AI_ALL) && defined(AI_V4MAPPED)
/*
* post-2553: AI_ALL and AI_V4MAPPED are effective only against
* AF_INET6 query. They need to be ignored if specified in other
@@ -451,6 +482,7 @@ getaddrinfo(hostname, servname, hints, res)
#endif
break;
}
+#endif
/*
* check for special cases. (1) numeric servname is disallowed if
@@ -459,7 +491,7 @@ getaddrinfo(hostname, servname, hints, res)
*/
if (MATCH_FAMILY(pai->ai_family, PF_INET, 1)
#ifdef PF_INET6
- || MATCH_FAMILY(pai->ai_family, PF_INET6, 1)
+ || MATCH_FAMILY(pai->ai_family, PF_INET6, 1)
#endif
) {
ai0 = *pai; /* backup *pai */
@@ -480,108 +512,182 @@ getaddrinfo(hostname, servname, hints, res)
ai0 = *pai;
- /* NULL hostname, or numeric hostname */
- for (ex = explore; ex->e_af >= 0; ex++) {
+ /*
+ * NULL hostname, or numeric hostname.
+ * If numreic representation of AF1 can be interpreted as FQDN
+ * representation of AF2, we need to think again about the code below.
+ */
+ found = 0;
+ for (afd = afdl; afd->a_af; afd++) {
*pai = ai0;
- /* PF_UNSPEC entries are prepared for DNS queries only */
- if (ex->e_af == PF_UNSPEC)
- continue;
-
- 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)))
+ if (!MATCH_FAMILY(pai->ai_family, afd->a_af, 1))
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;
+ pai->ai_family = afd->a_af;
- if (hostname == NULL)
- error = explore_null(pai, servname, &cur->ai_next);
- else
- error = explore_numeric_scope(pai, hostname, servname, &cur->ai_next);
-
- if (error)
- goto free;
+ if (hostname == NULL) {
+ /*
+ * filter out AFs that are not supported by the kernel
+ * XXX errno?
+ */
+ if (!addrconfig(pai->ai_family))
+ continue;
+ error = explore_null(pai, servname,
+ &afailist[afd - afdl]);
+ } else
+ error = explore_numeric_scope(pai, hostname, servname,
+ &afailist[afd - afdl]);
- while (cur && cur->ai_next)
- cur = cur->ai_next;
+ if (!error && afailist[afd - afdl])
+ found++;
+ }
+ if (found) {
+ numeric = 1;
+ goto globcopy;
}
-
- /*
- * 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 (hostname == NULL)
ERR(EAI_NONAME); /* used to be EAI_NODATA */
if (pai->ai_flags & AI_NUMERICHOST)
ERR(EAI_NONAME);
- if ((pai->ai_flags & AI_ADDRCONFIG) != 0 && !addrconfig(&ai0))
- ERR(EAI_FAIL);
-
/*
* hostname as alphabetical name.
- * we would like to prefer AF_INET6 than AF_INET, so we'll make a
- * outer loop by AFs.
+ * first, try to query DNS for all possible address families.
*/
+ /*
+ * the operating systems support PF_UNSPEC lookup in explore_fqdn().
+ */
+ *pai = ai0;
+ error = explore_fqdn(pai, hostname, servname, &afai_unspec);
+
+globcopy:
for (ex = explore; ex->e_af >= 0; ex++) {
*pai = ai0;
- /* require exact match for family field */
- if (pai->ai_family != ex->e_af)
+ if (pai->ai_family == PF_UNSPEC)
+ pai->ai_family = ex->e_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 (!MATCH(pai->ai_socktype, ex->e_socktype,
- WILD_SOCKTYPE(ex))) {
+#ifdef AI_ADDRCONFIG
+ /*
+ * If AI_ADDRCONFIG is specified, check if we are
+ * expected to return the address family or not.
+ */
+ if ((pai->ai_flags & AI_ADDRCONFIG) != 0 &&
+ !addrconfig(afd->a_af))
continue;
- }
- if (!MATCH(pai->ai_protocol, ex->e_protocol,
- WILD_PROTOCOL(ex))) {
+#endif
+
+ if ((pai->ai_flags & AI_PASSIVE) != 0 && WILD_PASSIVE(ex))
+ ;
+ else if ((pai->ai_flags & AI_PASSIVE) == 0 && WILD_ACTIVE(ex))
+ ;
+ else
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);
+ /*
+ * if the servname does not match socktype/protocol, ignore it.
+ */
+ if (get_portmatch(pai, servname) != 0)
+ continue;
+
+ if (afai_unspec)
+ afai = afai_unspec;
+ else {
+ if ((afd = find_afd(pai->ai_family)) == NULL)
+ continue;
+ /* XXX assumes that afd points inside afdl[] */
+ afai = afailist[afd - afdl];
+ }
+ if (!afai)
+ continue;
+
+ error = explore_copy(pai, afai, &cur->ai_next);
while (cur && cur->ai_next)
cur = cur->ai_next;
}
- /* XXX */
+ /* XXX inhibit errors if we have the result */
if (sentinel.ai_next)
error = 0;
- if (error)
- goto free;
+ /*
+ * ensure we return either:
+ * - error == 0, non-NULL *res
+ * - error != 0, NULL *res
+ */
if (error == 0) {
if (sentinel.ai_next) {
- good:
*res = sentinel.ai_next;
- return SUCCESS;
+ error = 0;
} else
error = EAI_FAIL;
}
- free:
- bad:
- if (sentinel.ai_next)
- freeaddrinfo(sentinel.ai_next);
- *res = NULL;
+
+bad:
+ if (afai_unspec)
+ freeaddrinfo(afai_unspec);
+ for (afd = afdl; afd->a_af; afd++) {
+ if (afailist[afd - afdl])
+ freeaddrinfo(afailist[afd - afdl]);
+ }
+ if (!*res)
+ if (sentinel.ai_next)
+ freeaddrinfo(sentinel.ai_next);
+ return error;
+}
+
+static int
+explore_copy(pai, src0, res)
+ const struct addrinfo *pai; /* seed */
+ const struct addrinfo *src0; /* source */
+ struct addrinfo **res;
+{
+ int error;
+ struct addrinfo sentinel, *cur;
+ const struct addrinfo *src;
+
+ error = 0;
+ sentinel.ai_next = NULL;
+ cur = &sentinel;
+
+ for (src = src0; src != NULL; src = src->ai_next) {
+ if (src->ai_family != pai->ai_family)
+ continue;
+
+ cur->ai_next = copy_ai(src);
+ if (!cur->ai_next) {
+ error = EAI_MEMORY;
+ goto fail;
+ }
+
+ cur->ai_next->ai_socktype = pai->ai_socktype;
+ cur->ai_next->ai_protocol = pai->ai_protocol;
+ cur = cur->ai_next;
+ }
+
+ *res = sentinel.ai_next;
+ return 0;
+
+fail:
+ freeaddrinfo(sentinel.ai_next);
return error;
}
@@ -596,7 +702,6 @@ explore_null(pai, servname, res)
const char *servname;
struct addrinfo **res;
{
- int s;
const struct afd *afd;
struct addrinfo *cur;
struct addrinfo sentinel;
@@ -607,17 +712,6 @@ explore_null(pai, servname, res)
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) {
- if (errno != EMFILE)
- return 0;
- } else
- _close(s);
-
- /*
* if the servname does not match socktype/protocol, ignore it.
*/
if (get_portmatch(pai, servname) != 0)
@@ -655,11 +749,12 @@ free:
* numeric hostname
*/
static int
-explore_numeric(pai, hostname, servname, res)
+explore_numeric(pai, hostname, servname, res, canonname)
const struct addrinfo *pai;
const char *hostname;
const char *servname;
struct addrinfo **res;
+ const char *canonname;
{
const struct afd *afd;
struct addrinfo *cur;
@@ -671,12 +766,6 @@ explore_numeric(pai, hostname, servname, res)
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;
@@ -689,6 +778,14 @@ explore_numeric(pai, hostname, servname, res)
pai->ai_family == PF_UNSPEC /*?*/) {
GET_AI(cur->ai_next, afd, pton);
GET_PORT(cur->ai_next, servname);
+ if ((pai->ai_flags & AI_CANONNAME)) {
+ /*
+ * Set the numeric address itself as
+ * the canonical name, based on a
+ * clarification in rfc2553bis-03.
+ */
+ GET_CANONNAME(cur->ai_next, canonname);
+ }
while (cur && cur->ai_next)
cur = cur->ai_next;
} else
@@ -702,6 +799,14 @@ explore_numeric(pai, hostname, servname, res)
pai->ai_family == PF_UNSPEC /*?*/) {
GET_AI(cur->ai_next, afd, pton);
GET_PORT(cur->ai_next, servname);
+ if ((pai->ai_flags & AI_CANONNAME)) {
+ /*
+ * Set the numeric address itself as
+ * the canonical name, based on a
+ * clarification in rfc2553bis-03.
+ */
+ GET_CANONNAME(cur->ai_next, canonname);
+ }
while (cur && cur->ai_next)
cur = cur->ai_next;
} else
@@ -731,7 +836,7 @@ explore_numeric_scope(pai, hostname, servname, res)
struct addrinfo **res;
{
#if !defined(SCOPE_DELIMITER) || !defined(INET6)
- return explore_numeric(pai, hostname, servname, res);
+ return explore_numeric(pai, hostname, servname, res, hostname);
#else
const struct afd *afd;
struct addrinfo *cur;
@@ -739,22 +844,16 @@ explore_numeric_scope(pai, hostname, servname, res)
char *cp, *hostname2 = NULL, *scope, *addr;
struct sockaddr_in6 *sin6;
- /*
- * 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);
+ return explore_numeric(pai, hostname, servname, res, hostname);
cp = strchr(hostname, SCOPE_DELIMITER);
if (cp == NULL)
- return explore_numeric(pai, hostname, servname, res);
+ return explore_numeric(pai, hostname, servname, res, hostname);
/*
* Handle special case of <scoped_address><delimiter><scope id>
@@ -767,7 +866,7 @@ explore_numeric_scope(pai, hostname, servname, res)
addr = hostname2;
scope = cp + 1;
- error = explore_numeric(pai, addr, servname, res);
+ error = explore_numeric(pai, addr, servname, res, hostname);
if (error == 0) {
u_int32_t scopeid;
@@ -777,6 +876,8 @@ explore_numeric_scope(pai, hostname, servname, res)
sin6 = (struct sockaddr_in6 *)(void *)cur->ai_addr;
if (ip6_str2scopeid(scope, sin6, &scopeid) == -1) {
free(hostname2);
+ freeaddrinfo(*res);
+ *res = NULL;
return(EAI_NONAME); /* XXX: is return OK? */
}
sin6->sin6_scope_id = scopeid;
@@ -785,6 +886,10 @@ explore_numeric_scope(pai, hostname, servname, res)
free(hostname2);
+ if (error && *res) {
+ freeaddrinfo(*res);
+ *res = NULL;
+ }
return error;
#endif
}
@@ -796,10 +901,9 @@ get_canonname(pai, ai, str)
const char *str;
{
if ((pai->ai_flags & AI_CANONNAME) != 0) {
- ai->ai_canonname = (char *)malloc(strlen(str) + 1);
+ ai->ai_canonname = strdup(str);
if (ai->ai_canonname == NULL)
return EAI_MEMORY;
- strlcpy(ai->ai_canonname, str, strlen(str) + 1);
}
return 0;
}
@@ -875,6 +979,39 @@ get_ai(pai, afd, addr)
return ai;
}
+/* XXX need to malloc() the same way we do from other functions! */
+static struct addrinfo *
+copy_ai(pai)
+ const struct addrinfo *pai;
+{
+ struct addrinfo *ai;
+ size_t l;
+
+ l = sizeof(*ai) + pai->ai_addrlen;
+ if ((ai = (struct addrinfo *)malloc(l)) == NULL)
+ return NULL;
+ memset(ai, 0, l);
+ memcpy(ai, pai, sizeof(*ai));
+ ai->ai_addr = (struct sockaddr *)(void *)(ai + 1);
+ memcpy(ai->ai_addr, pai->ai_addr, pai->ai_addrlen);
+
+ if (pai->ai_canonname) {
+ l = strlen(pai->ai_canonname) + 1;
+ if ((ai->ai_canonname = malloc(l)) == NULL) {
+ free(ai);
+ return NULL;
+ }
+ strlcpy(ai->ai_canonname, pai->ai_canonname, l);
+ } else {
+ /* just to make sure */
+ ai->ai_canonname = NULL;
+ }
+
+ ai->ai_next = NULL;
+
+ return ai;
+}
+
static int
get_portmatch(ai, servname)
const struct addrinfo *ai;
@@ -914,6 +1051,7 @@ get_port(ai, servname, matchonly)
return EAI_SERVICE;
case SOCK_DGRAM:
case SOCK_STREAM:
+ case SOCK_SEQPACKET:
allownumeric = 1;
break;
case ANY:
@@ -931,11 +1069,11 @@ get_port(ai, servname, matchonly)
return EAI_SERVICE;
port = htons(port);
} else {
- switch (ai->ai_socktype) {
- case SOCK_DGRAM:
+ switch (ai->ai_protocol) {
+ case IPPROTO_UDP:
proto = "udp";
break;
- case SOCK_STREAM:
+ case IPPROTO_TCP:
proto = "tcp";
break;
default:
@@ -986,41 +1124,20 @@ find_afd(af)
* will take care of it.
* the semantics of AI_ADDRCONFIG is not defined well. we are not sure
* if the code is right or not.
- *
- * XXX PF_UNSPEC -> PF_INET6 + PF_INET mapping needs to be in sync with
- * _dns_getaddrinfo.
*/
static int
-addrconfig(pai)
- struct addrinfo *pai;
+addrconfig(af)
+ int af;
{
- int s, af;
+ int s;
- /*
- * 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.
- */
- af = pai->ai_family;
- if (af == AF_UNSPEC) {
- if ((s = _socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
- af = AF_INET;
- else {
- _close(s);
- if ((s = _socket(AF_INET, SOCK_DGRAM, 0)) < 0)
- af = AF_INET6;
- else
- _close(s);
- }
- }
- if (af != AF_UNSPEC) {
- if ((s = _socket(af, SOCK_DGRAM, 0)) < 0)
+ /* XXX errno */
+ s = socket(af, SOCK_DGRAM, 0);
+ if (s < 0) {
+ if (errno != EMFILE)
return 0;
- _close(s);
- }
- pai->ai_family = af;
+ } else
+ close(s);
return 1;
}
@@ -1033,16 +1150,15 @@ ip6_str2scopeid(scope, sin6, scopeid)
u_int32_t *scopeid;
{
u_long lscopeid;
- struct in6_addr *a6;
+ struct in6_addr *a6 = &sin6->sin6_addr;
char *ep;
- a6 = &sin6->sin6_addr;
-
/* empty scopeid portion is invalid */
if (*scope == '\0')
return -1;
- if (IN6_IS_ADDR_LINKLOCAL(a6) || IN6_IS_ADDR_MC_LINKLOCAL(a6)) {
+ if (IN6_IS_ADDR_LINKLOCAL(a6) || IN6_IS_ADDR_MC_LINKLOCAL(a6) ||
+ IN6_IS_ADDR_MC_NODELOCAL(a6)) {
/*
* We currently assume a one-to-one mapping between links
* and interfaces, so we simply use interface indices for
@@ -1063,7 +1179,7 @@ ip6_str2scopeid(scope, sin6, scopeid)
goto trynumeric; /* global */
/* try to convert to a numeric id as a last resort */
- trynumeric:
+trynumeric:
errno = 0;
lscopeid = strtoul(scope, &ep, 10);
*scopeid = (u_int32_t)(lscopeid & 0xffffffffUL);
@@ -1699,9 +1815,13 @@ nextline:
*cp++ = '\0';
}
- hints = *pai;
+ /* we should not glob socktype/protocol here */
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = pai->ai_family;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_protocol = 0;
hints.ai_flags = AI_NUMERICHOST;
- error = getaddrinfo(addr, NULL, &hints, &res0);
+ error = getaddrinfo(addr, "0", &hints, &res0);
if (error == 0) {
for (res = res0; res; res = res->ai_next) {
/* cover it up */
diff --git a/lib/libc/net/inet6_opt_init.3 b/lib/libc/net/inet6_opt_init.3
new file mode 100644
index 0000000..65af709
--- /dev/null
+++ b/lib/libc/net/inet6_opt_init.3
@@ -0,0 +1,291 @@
+.\" $KAME: inet6_opt_init.3,v 1.5 2002/10/17 14:13:47 jinmei Exp $
+.\"
+.\" Copyright (C) 2000 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$
+.\"
+.Dd February 5, 2000
+.Dt INET6_OPT_INIT 3
+.Os
+.\"
+.Sh NAME
+.Nm inet6_opt_init ,
+.Nm inet6_opt_append ,
+.Nm inet6_opt_finish ,
+.Nm inet6_opt_set_val ,
+.Nm inet6_opt_next ,
+.Nm inet6_opt_find ,
+.Nm inet6_opt_get_val
+.Nd IPv6 Hop-by-Hop and Destination Options manipulation
+.\"
+.Sh SYNOPSIS
+.In netinet/in.h
+.Ft "int"
+.Fn inet6_opt_init "void *extbuf" "socklen_t extlen"
+.Ft "int"
+.Fn inet6_opt_append "void *extbuf" "socklen_t extlen" "int offset" "u_int8_t type" "socklen_t len" "u_int8_t align" "void **databufp"
+.Ft "int"
+.Fn inet6_opt_finish "void *extbuf" "socklen_t extlen" "int offset"
+.Ft "int"
+.Fn inet6_opt_set_val "void *databuf" "int offset" "void *val" "socklen_t vallen"
+.Ft "int"
+.Fn inet6_opt_next "void *extbuf" "socklen_t extlen" "int offset" "u_int8_t *typep" "socklen_t *lenp" "void **databufp"
+.Ft "int"
+.Fn inet6_opt_find "void *extbuf" "socklen_t extlen" "int offset" "u_int8_t type" "socklen_t *lenp" "void **databufp"
+.Ft "int"
+.Fn inet6_opt_get_val "void *databuf" "socklen_t offset" "void *val" "socklen_t vallen"
+.\"
+.Sh DESCRIPTION
+Building and parsing the Hop-by-Hop and Destination options is
+complicated.
+The advanced API therefore defines a set
+of functions to help applications.
+These functions assume the
+formatting rules specified in Appendix B in RFC2460 i.e. that the
+largest field is placed last in the option.
+The function prototypes for
+these functions are all in the
+.Aq Li netinet/in.h
+header.
+.\"
+.Ss inet6_opt_init
+.Fn inet6_opt_init
+returns the number of bytes needed for the empty
+extension header i.e. without any options.
+If
+.Li extbuf
+is not NULL it also initializes the extension header to have the correct length
+field.
+In that case if the
+.Li extlen value is not a positive
+.Po
+i.e., non-zero
+.Pc
+multiple of 8 the function fails and returns -1.
+.\"
+.Ss inet6_opt_append
+.Fn inet6_opt_append
+returns the updated total length taking into account
+adding an option with length
+.Li len
+and alignment
+.Li align .
+.Li Offset
+should be the length returned by
+.Fn inet6_opt_init
+or a previous
+.Fn inet6_opt_append .
+If
+.Li extbuf
+is not NULL then, in addition to returning the length,
+the function inserts any needed pad option, initializes the option
+.Po
+setting the type and length fields
+.Pc
+and returns a pointer to the location for the option content in
+.Li databufp .
+.Pp
+.Li type
+is the 8-bit option type.
+.Li len
+is the length of the option data
+.Po
+i.e. excluding the option type and option length fields.
+.Pc
+.Pp
+Once
+.Fn inet6_opt_append
+has been called the application can use the
+databuf directly, or use
+.Fn inet6_opt_set_val
+to specify the content of the option.
+.Pp
+The option type must have a value from 2 to 255, inclusive.
+.Po
+0 and 1 are reserved for the Pad1 and PadN options, respectively.
+.Pc
+.Pp
+The option data length must have a value between 0 and 255,
+inclusive, and is the length of the option data that follows.
+.Pp
+The
+.Li align
+parameter must have a value of 1, 2, 4, or 8.
+The align value can not exceed the value of
+.Li len .
+.\"
+.Ss inet6_opt_finish
+.Fn inet6_opt_finish
+returns the updated total length
+taking into account the final padding of the extension header to make
+it a multiple of 8 bytes.
+.Li Offset
+should be the length returned by
+.Fn inet6_opt_init
+or
+.Fn inet6_opt_append .
+If
+.Li extbuf
+is not NULL the function also
+initializes the option by inserting a Pad1 or PadN option of the
+proper length.
+.Pp
+If the necessary pad does not fit in the extension header buffer the
+function returns -1.
+.\"
+.Ss inet6_opt_set_val
+.Fn inet6_opt_set_val
+inserts data items of various sizes in the data portion of the option.
+.Li Databuf
+should be a pointer returned by
+.Fn inet6_opt_append .
+.Li val
+should point to the data to be
+inserted.
+.Li Offset
+specifies where in the data portion of the option
+the value should be inserted; the first byte after the option type
+and length is accessed by specifying an offset of zero.
+.Pp
+The caller should ensure that each field is aligned on its natural
+boundaries as described in Appendix B of RFC2460, but the function
+must not rely on the caller's behavior.
+Even when the alignment requirement is not satisfied,
+the function should just copy the data as required.
+.Pp
+The function returns the offset for the next field
+.Po
+i.e.,
+.Li offset
++
+.Li vallen
+.Pc
+which can be used when composing option content with multiple fields.
+.\"
+.Ss inet6_opt_next
+.Fn inet6_opt_next
+parses received extension headers returning the next
+option.
+.Li Extbuf
+and
+.Li extlen
+specifies the extension header.
+.Li Offset
+should either be zero (for the first option) or the length returned
+by a previous call to
+.Fn inet6_opt_next
+or
+.Fn inet6_opt_find .
+It specifies the position where to continue scanning the extension
+buffer.
+The next option is returned by updating
+.Li typep ,
+.Li lenp ,
+and
+.Li databufp .
+This function returns the updated
+.Dq previous
+length
+computed by advancing past the option that was returned.
+This returned
+.Dq previous
+length can then be passed to subsequent calls to
+.Fn inet6_opt_next .
+This function does not return any PAD1 or PADN options.
+When there are no more options the return value is -1.
+.\"
+.Ss inet6_opt_get_val
+.Fn inet6_opt_get_val
+This function extracts data items of various sizes
+in the data portion of the option.
+.Li Databuf
+should be a pointer returned by
+.Fn inet6_opt_next
+or
+.Fn inet6_opt_find .
+.Li Val
+should point to the destination for the extracted data.
+.Li Offset
+specifies from where in the data portion of the option the value should be
+extracted; the first byte after the option type and length is
+accessed by specifying an offset of zero.
+.Pp
+It is expected that each field is aligned on its natural boundaries
+as described in Appendix B of RFC2460, but the function must not
+rely on the alignment.
+.Pp
+The function returns the offset for the next field
+.Po
+i.e.,
+.Li offset
++
+.Li vallen
+.Pc
+which can be used when extracting option content with
+multiple fields.
+Robust receivers might want to verify alignment before calling
+this function.
+.\"
+.Sh DIAGNOSTICS
+All the functions ruturn
+.Li -1
+on an error.
+.\"
+.Sh EXAMPLES
+draft-ietf-ipngwg-rfc2292bis-08.txt
+gives comprehensive examples in Section 23.
+.Pp
+KAME also provides examples in the advapitest directry of its kit.
+.\"
+.Sh SEE ALSO
+.Rs
+.%A W. Stevens
+.%A M. Thomas
+.%A E. Nordmark
+.%A T. Jinmei
+.%T "Advanced Sockets API for IPv6"
+.%N draft-ietf-ipngwg-rfc2292bis-08
+.%D October 2002
+.Re
+.Rs
+.%A S. Deering
+.%A R. Hinden
+.%T "Internet Protocol, Version 6 (IPv6) Specification"
+.%N RFC2460
+.%D December 1998
+.Re
+.Sh HISTORY
+The implementation first appeared in KAME advanced networking kit.
+.Sh STANDARDS
+The functions
+are documented in
+.Dq Advanced Sockets API for IPv6
+.Pq draft-ietf-ipngwg-rfc2292bis-08.txt .
+.\"
+.Sh BUGS
+The text was shamelessly copied from internet-drafts for RFC2292bis.
diff --git a/lib/libc/net/inet6_rth_space.3 b/lib/libc/net/inet6_rth_space.3
new file mode 100644
index 0000000..1289b11
--- /dev/null
+++ b/lib/libc/net/inet6_rth_space.3
@@ -0,0 +1,254 @@
+.\" $KAME: kame/kame/kame/libinet6/inet6_rth_space.3,v 1.4 2002/10/17 14:13:48 jinmei Exp $
+.\" $FreeBSD$
+.\"
+.\" Copyright (C) 2000 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$
+.\"
+.Dd February 5, 2000
+.Dt INET6_RTH_SPACE 3
+.Os
+.\"
+.Sh NAME
+.Nm inet6_rth_space
+.Nm inet6_rth_init
+.Nm inet6_rth_add
+.Nm inet6_rth_reverse
+.Nm inet6_rth_segments
+.Nm inet6_rth_getaddr
+.Nd IPv6 Routing Header Options manipulation
+.\"
+.Sh SYNOPSIS
+.In netinet/in.h
+.Ft socklen_t
+.Fn inet6_rth_space "int" "int"
+.Ft "void *"
+.Fn inet6_rth_init "void *" "socklen_t" "int" "int"
+.Ft int
+.Fn inet6_rth_add "void *" "const struct in6_addr *"
+.Ft int
+.Fn inet6_rth_reverse "const void *" "void *"
+.Ft int
+.Fn inet6_rth_segments "const void *"
+.Ft "struct in6_addr *"
+.Fn inet6_rth_getaddr "const void *" "int"
+.\"
+.Sh DESCRIPTION
+The IPv6 advanced API defines six
+functions that the application calls to build and examine a Routing
+header, and the ability to use sticky options or ancillary data to
+communicate this information between the application and the kernel
+using the IPV6_RTHDR option.
+.Pp
+Three functions build a Routing header:
+.Bl -hang
+.It Fn inet6_rth_space
+returns #bytes required for Routing header
+.It Fn inet6_rth_init
+initializes buffer data for Routing header
+.It Fn inet6_rth_add
+adds one IPv6 address to the Routing header
+.El
+.Pp
+Three functions deal with a returned Routing header:
+.Bl -hang
+.It Fn inet6_rth_reverse
+reverses a Routing header
+.It Fn inet6_rth_segments
+returns #segments in a Routing header
+.It Fn inet6_rth_getaddr
+fetches one address from a Routing header
+.El
+.Pp
+The function prototypes for these functions are defined as a result
+of including the
+.Aq Li netinet/in.h
+header.
+.\"
+.Ss inet6_rth_space
+.Fn inet6_rth_space
+returns the number of bytes required to hold a Routing
+header of the specified type containing the specified number of
+.Li segments
+.Po addresses.
+.Pc
+For an IPv6 Type 0 Routing header, the number
+of
+.Li segments
+must be between 0 and 127, inclusive.
+The return value is just the space for the Routing header.
+When the application uses
+ancillary data it must pass the returned length to
+.Fn CMSG_LEN
+to determine how much memory is needed for the ancillary data object
+.Po
+including the cmsghdr structure.
+.Pc
+.Pp
+If the return value is 0, then either the type of the Routing header
+is not supported by this implementation or the number of segments is
+invalid for this type of Routing header.
+.Pp
+Note: This function returns the size but does not allocate the space
+required for the ancillary data.
+This allows an application to
+allocate a larger buffer, if other ancillary data objects are
+desired, since all the ancillary data objects must be specified to
+.Fn sendmsg
+as a single msg_control buffer.
+.Ss inet6_rth_init
+.Fn inet6_rth_init
+initializes the buffer pointed to by
+.Li bp
+to contain a
+Routing header of the specified type and sets ip6r_len based on the
+.Li segments
+parameter.
+.Li bp_len
+is only used to verify that the buffer is
+large enough.
+The ip6r_segleft field is set to zero;
+.Fn inet6_rth_add
+will increment it.
+.Pp
+When the application uses ancillary data the application must
+initialize any cmsghdr fields.
+.Pp
+The caller must allocate the buffer and its size can be determined by
+calling
+.Fn inet6_rth_space .
+.Pp
+Upon success the return value is the pointer to the buffer
+.Li bp ,
+and this is then used as the first argument to the next two functions.
+Upon an error the return value is NULL.
+.\"
+.Ss inet6_rth_add
+.Fn inet6_rth_add
+adds the IPv6 address pointed to by
+.Li addr
+to the end of the Routing header being constructed.
+.Pp
+If successful, the segleft member of the Routing Header is updated to
+account for the new address in the Routing header and the return
+value of the function is 0.
+Upon an error the return value of the function is -1.
+.\"
+.Ss inet6_rth_reverse
+.Fn inet6_rth_reverse
+takes a Routing header extension header
+.Po
+pointed to by the first argument
+.Li in
+.Pc
+and writes a new Routing header that sends
+datagrams along the reverse of that route.
+Both arguments are allowed to point to the same buffer
+.Po
+that is, the reversal can occur in place.
+.Pc
+.Pp
+The return value of the function is 0 on success, or -1 upon an error.
+.\"
+.Ss inet6_rth_segments
+.Fn inet6_rth_segments
+returns the number of segments
+.Po
+addresses
+.Pc
+contained in the Routing header described by
+.Li bp .
+On success the return value is
+zero or greater.
+The return value of the function is -1 upon an error.
+.\"
+.Ss inet6_rth_getaddr
+.Fn inet6_rth_getaddr
+returns a pointer to the IPv6 address specified by
+.Li index
+.Po
+which must have a value between 0 and one less than the value
+returned by
+.Fn inet6_rth_segments
+.Pc
+in the Routing header described by
+.Li bp .
+An application should first call
+.Fn inet6_rth_segments
+to obtain the number of segments in the Routing header.
+.Pp
+Upon an error the return value of the function is NULL.
+.\"
+.Sh DIAGNOSTICS
+.Fn inet6_rth_space
+and
+.FN inet6_rth_getaddr
+return 0 on errors.
+.Pp
+.Fn inet6_rthdr_init
+returns
+.Dv NULL
+on error.
+.Fn inet6_rth_add
+and
+.Fn inet6_rth_reverse
+return0 on success, or -1 upon an error.
+.\"
+.Sh EXAMPLES
+draft-ietf-ipngwg-rfc2292bis-08.txt
+gives comprehensive examples in Section 22.
+.Pp
+KAME also provides examples in the advapitest directry of its kit.
+.\"
+.Sh SEE ALSO
+.Rs
+.%A W. Stevens
+.%A M. Thomas
+.%A E. Nordmark
+.%A E. Jinmei
+.%T "Advanced Sockets API for IPv6"
+.%N draft-ietf-ipngwg-rfc2292bis-08
+.%D October 2002
+.Re
+.Rs
+.%A S. Deering
+.%A R. Hinden
+.%T "Internet Protocol, Version 6 (IPv6) Specification"
+.%N RFC2460
+.%D December 1998
+.Re
+.Sh HISTORY
+The implementation first appeared in KAME advanced networking kit.
+.Sh STANDARDS
+The functions
+are documented in
+.Dq Advanced Sockets API for IPv6
+.Pq draft-ietf-ipngwg-rfc2292bis-08.txt .
+.\"
+.Sh BUGS
+The text was shamelessly copied from internet-drafts for RFC2292bis.
diff --git a/lib/libc/net/ip6opt.c b/lib/libc/net/ip6opt.c
index a26e45d..2f54918 100644
--- a/lib/libc/net/ip6opt.c
+++ b/lib/libc/net/ip6opt.c
@@ -1,3 +1,5 @@
+/* $KAME: ip6opt.c,v 1.13 2003/06/06 10:08:20 suz Exp $ */
+
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
* All rights reserved.
@@ -111,6 +113,8 @@ inet6_option_append(cmsg, typep, multx, plusy)
return(-1);
if (plusy < 0 || plusy > 7)
return(-1);
+ if (typep[0] > 255)
+ return(-1);
/*
* If this is the first option, allocate space for the
@@ -127,6 +131,7 @@ inet6_option_append(cmsg, typep, multx, plusy)
padlen = (((off % multx) + (multx - 1)) & ~(multx - 1)) -
(off % multx);
padlen += plusy;
+ padlen %= multx; /* keep the pad as short as possible */
/* insert padding */
inet6_insert_padopt(bp, padlen);
cmsg->cmsg_len += padlen;
@@ -200,6 +205,7 @@ inet6_option_alloc(cmsg, datalen, multx, plusy)
padlen = (((off % multx) + (multx - 1)) & ~(multx - 1)) -
(off % multx);
padlen += plusy;
+ padlen %= multx; /* keep the pad as short as possible */
/* insert padding */
inet6_insert_padopt(bp, padlen);
cmsg->cmsg_len += padlen;
@@ -383,3 +389,224 @@ inet6_insert_padopt(u_char *p, int len)
return;
}
}
+
+/*
+ * The following functions are defined in a successor of RFC2292, aka
+ * rfc2292bis.
+ */
+
+int
+inet6_opt_init(void *extbuf, socklen_t extlen)
+{
+ struct ip6_ext *ext = (struct ip6_ext *)extbuf;
+
+ if (extlen < 0 || (extlen % 8))
+ return(-1);
+
+ if (ext) {
+ if (extlen == 0)
+ return(-1);
+ ext->ip6e_len = (extlen >> 3) - 1;
+ }
+
+ return(2); /* sizeof the next and the length fields */
+}
+
+int
+inet6_opt_append(void *extbuf, socklen_t extlen, int offset, u_int8_t type,
+ socklen_t len, u_int8_t align, void **databufp)
+{
+ int currentlen = offset, padlen = 0;
+
+ /*
+ * The option type must have a value from 2 to 255, inclusive.
+ * (0 and 1 are reserved for the Pad1 and PadN options, respectively.)
+ */
+ if (type < 2 || type > 255)
+ return(-1);
+
+ /*
+ * The option data length must have a value between 0 and 255,
+ * inclusive, and is the length of the option data that follows.
+ */
+ if (len < 0 || len > 255)
+ return(-1);
+
+ /*
+ * The align parameter must have a value of 1, 2, 4, or 8.
+ * The align value can not exceed the value of len.
+ */
+ if (align != 1 && align != 2 && align != 4 && align != 8)
+ return(-1);
+ if (align > len)
+ return(-1);
+
+ /* Calculate the padding length. */
+ currentlen += 2 + len; /* 2 means "type + len" */
+ if (currentlen % align)
+ padlen = align - (currentlen % align);
+
+ /* The option must fit in the extension header buffer. */
+ currentlen += padlen;
+ if (extlen && /* XXX: right? */
+ currentlen > extlen)
+ return(-1);
+
+ if (extbuf) {
+ u_int8_t *optp = (u_int8_t *)extbuf + offset;
+
+ if (padlen == 1) {
+ /* insert a Pad1 option */
+ *optp = IP6OPT_PAD1;
+ optp++;
+ }
+ else if (padlen > 0) {
+ /* insert a PadN option for alignment */
+ *optp++ = IP6OPT_PADN;
+ *optp++ = padlen - 2;
+ memset(optp, 0, padlen - 2);
+ optp += (padlen - 2);
+ }
+
+ *optp++ = type;
+ *optp++ = len;
+
+ *databufp = optp;
+ }
+
+ return(currentlen);
+}
+
+int
+inet6_opt_finish(void *extbuf, socklen_t extlen, int offset)
+{
+ int updatelen = offset > 0 ? (1 + ((offset - 1) | 7)) : 0;;
+
+ if (extbuf) {
+ u_int8_t *padp;
+ int padlen = updatelen - offset;
+
+ if (updatelen > extlen)
+ return(-1);
+
+ padp = (u_int8_t *)extbuf + offset;
+ if (padlen == 1)
+ *padp = IP6OPT_PAD1;
+ else if (padlen > 0) {
+ *padp++ = IP6OPT_PADN;
+ *padp++ = (padlen - 2);
+ memset(padp, 0, padlen - 2);
+ }
+ }
+
+ return(updatelen);
+}
+
+int
+inet6_opt_set_val(void *databuf, int offset, void *val, socklen_t vallen)
+{
+
+ memcpy((u_int8_t *)databuf + offset, val, vallen);
+ return(offset + vallen);
+}
+
+int
+inet6_opt_next(void *extbuf, socklen_t extlen, int offset, u_int8_t *typep,
+ size_t *lenp, void **databufp)
+{
+ u_int8_t *optp, *lim;
+ int optlen;
+
+ /* Validate extlen. XXX: is the variable really necessary?? */
+ if (extlen == 0 || (extlen % 8))
+ return(-1);
+ lim = (u_int8_t *)extbuf + extlen;
+
+ /*
+ * If this is the first time this function called for this options
+ * header, simply return the 1st option.
+ * Otherwise, search the option list for the next option.
+ */
+ if (offset == 0) {
+ optp = (u_int8_t *)((struct ip6_hbh *)extbuf + 1);
+ }
+ else
+ optp = (u_int8_t *)extbuf + offset;
+
+ /* Find the next option skipping any padding options. */
+ while(optp < lim) {
+ switch(*optp) {
+ case IP6OPT_PAD1:
+ optp++;
+ break;
+ case IP6OPT_PADN:
+ if ((optlen = ip6optlen(optp, lim)) == 0)
+ goto optend;
+ optp += optlen;
+ break;
+ default: /* found */
+ if ((optlen = ip6optlen(optp, lim)) == 0)
+ goto optend;
+ *typep = *optp;
+ *lenp = optlen - 2;
+ *databufp = optp + 2;
+ return(optp + optlen - (u_int8_t *)extbuf);
+ }
+ }
+
+ optend:
+ *databufp = NULL; /* for safety */
+ return(-1);
+}
+
+int
+inet6_opt_find(void *extbuf, socklen_t extlen, int offset, u_int8_t type,
+ socklen_t *lenp, void **databufp)
+{
+ u_int8_t *optp, *lim;
+ int optlen;
+
+ /* Validate extlen. XXX: is the variable really necessary?? */
+ if (extlen == 0 || (extlen % 8))
+ return(-1);
+ lim = (u_int8_t *)extbuf + extlen;
+
+ /*
+ * If this is the first time this function called for this options
+ * header, simply return the 1st option.
+ * Otherwise, search the option list for the next option.
+ */
+ if (offset == 0) {
+ optp = (u_int8_t *)((struct ip6_hbh *)extbuf + 1);
+ }
+ else
+ optp = (u_int8_t *)extbuf + offset;
+
+ /* Find the specified option */
+ while(optp < lim) {
+ if ((optlen = ip6optlen(optp, lim)) == 0)
+ goto optend;
+
+ if (*optp == type) { /* found */
+ *lenp = optlen - 2;
+ *databufp = optp + 2;
+ return(optp + optlen - (u_int8_t *)extbuf);
+ }
+
+ optp += optlen;
+ }
+
+ optend:
+ *databufp = NULL; /* for safety */
+ return(-1);
+}
+
+int
+inet6_opt_get_val(void *databuf, int offset, void *val, socklen_t vallen)
+{
+
+ /* we can't assume alignment here */
+ memcpy(val, (u_int8_t *)databuf + offset, vallen);
+
+ return(offset + vallen);
+}
diff --git a/lib/libc/net/rthdr.c b/lib/libc/net/rthdr.c
index abfe34a..d7b0077 100644
--- a/lib/libc/net/rthdr.c
+++ b/lib/libc/net/rthdr.c
@@ -1,4 +1,4 @@
-/* $KAME: rthdr.c,v 1.8 2001/08/20 02:32:40 itojun Exp $ */
+/* $KAME: rthdr.c,v 1.19 2003/06/06 10:48:51 itojun Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -42,272 +42,402 @@ __FBSDID("$FreeBSD$");
#include <string.h>
#include <stdio.h>
+/*
+ * RFC2292 API
+ */
+
size_t
inet6_rthdr_space(type, seg)
- int type, seg;
+ int type, seg;
{
- switch(type) {
- case IPV6_RTHDR_TYPE_0:
- if (seg < 1 || seg > 23)
- return(0);
- return(CMSG_SPACE(sizeof(struct in6_addr) * (seg - 1)
- + sizeof(struct ip6_rthdr0)));
- default:
-#ifdef DEBUG
- fprintf(stderr, "inet6_rthdr_space: unknown type(%d)\n", type);
+ switch (type) {
+ case IPV6_RTHDR_TYPE_0:
+ if (seg < 1 || seg > 23)
+ return (0);
+#ifdef COMPAT_RFC2292
+ return (CMSG_SPACE(sizeof(struct in6_addr) * (seg - 1) +
+ sizeof(struct ip6_rthdr0)));
+#else
+ return (CMSG_SPACE(sizeof(struct in6_addr) * seg +
+ sizeof(struct ip6_rthdr0)));
#endif
- return(0);
- }
+ default:
+ return (0);
+ }
}
struct cmsghdr *
inet6_rthdr_init(bp, type)
- void *bp;
- int type;
+ void *bp;
+ int type;
{
- struct cmsghdr *ch = (struct cmsghdr *)bp;
- struct ip6_rthdr *rthdr;
-
- rthdr = (struct ip6_rthdr *)CMSG_DATA(ch);
-
- ch->cmsg_level = IPPROTO_IPV6;
- ch->cmsg_type = IPV6_RTHDR;
-
- switch(type) {
- case IPV6_RTHDR_TYPE_0:
- ch->cmsg_len = CMSG_LEN(sizeof(struct ip6_rthdr0) - sizeof(struct in6_addr));
- bzero(rthdr, sizeof(struct ip6_rthdr0));
- rthdr->ip6r_type = IPV6_RTHDR_TYPE_0;
- return(ch);
- default:
-#ifdef DEBUG
- fprintf(stderr, "inet6_rthdr_init: unknown type(%d)\n", type);
+ struct cmsghdr *ch = (struct cmsghdr *)bp;
+ struct ip6_rthdr *rthdr;
+
+ rthdr = (struct ip6_rthdr *)CMSG_DATA(ch);
+
+ ch->cmsg_level = IPPROTO_IPV6;
+ ch->cmsg_type = IPV6_RTHDR;
+
+ switch (type) {
+ case IPV6_RTHDR_TYPE_0:
+#ifdef COMPAT_RFC2292
+ ch->cmsg_len = CMSG_LEN(sizeof(struct ip6_rthdr0) -
+ sizeof(struct in6_addr));
+#else
+ ch->cmsg_len = CMSG_LEN(sizeof(struct ip6_rthdr0));
#endif
- return(NULL);
- }
+
+ bzero(rthdr, sizeof(struct ip6_rthdr0));
+ rthdr->ip6r_type = IPV6_RTHDR_TYPE_0;
+ return (ch);
+ default:
+ return (NULL);
+ }
}
+/* ARGSUSED */
int
inet6_rthdr_add(cmsg, addr, flags)
- struct cmsghdr *cmsg;
- const struct in6_addr *addr;
- u_int flags;
+ struct cmsghdr *cmsg;
+ const struct in6_addr *addr;
+ u_int flags;
{
- struct ip6_rthdr *rthdr;
-
- rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
-
- switch(rthdr->ip6r_type) {
- case IPV6_RTHDR_TYPE_0:
- {
- struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
- if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT) {
-#ifdef DEBUG
- fprintf(stderr, "inet6_rthdr_add: unsupported flag(%d)\n", flags);
-#endif
- return(-1);
- }
- if (rt0->ip6r0_segleft == 23) {
-#ifdef DEBUG
- fprintf(stderr, "inet6_rthdr_add: segment overflow\n");
-#endif
- return(-1);
- }
- if (flags == IPV6_RTHDR_STRICT) {
- int c, b;
- c = rt0->ip6r0_segleft / 8;
- b = rt0->ip6r0_segleft % 8;
- rt0->ip6r0_slmap[c] |= (1 << (7 - b));
- }
- rt0->ip6r0_segleft++;
- bcopy(addr, (caddr_t)rt0 + ((rt0->ip6r0_len + 1) << 3),
- sizeof(struct in6_addr));
- rt0->ip6r0_len += sizeof(struct in6_addr) >> 3;
- cmsg->cmsg_len = CMSG_LEN((rt0->ip6r0_len + 1) << 3);
- break;
- }
- default:
-#ifdef DEBUG
- fprintf(stderr, "inet6_rthdr_add: unknown type(%d)\n",
- rthdr->ip6r_type);
+ struct ip6_rthdr *rthdr;
+
+ rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
+
+ switch (rthdr->ip6r_type) {
+ case IPV6_RTHDR_TYPE_0:
+ {
+ struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
+ if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT)
+ return (-1);
+ if (rt0->ip6r0_segleft == 23)
+ return (-1);
+
+#ifdef COMPAT_RFC1883 /* XXX */
+ if (flags == IPV6_RTHDR_STRICT) {
+ int c, b;
+ c = rt0->ip6r0_segleft / 8;
+ b = rt0->ip6r0_segleft % 8;
+ rt0->ip6r0_slmap[c] |= (1 << (7 - b));
+ }
+#else
+ if (flags != IPV6_RTHDR_LOOSE)
+ return (-1);
#endif
- return(-1);
- }
+ rt0->ip6r0_segleft++;
+ bcopy(addr, (caddr_t)rt0 + ((rt0->ip6r0_len + 1) << 3),
+ sizeof(struct in6_addr));
+ rt0->ip6r0_len += sizeof(struct in6_addr) >> 3;
+ cmsg->cmsg_len = CMSG_LEN((rt0->ip6r0_len + 1) << 3);
+ break;
+ }
+ default:
+ return (-1);
+ }
- return(0);
+ return (0);
}
+/* ARGSUSED */
int
inet6_rthdr_lasthop(cmsg, flags)
- struct cmsghdr *cmsg;
- unsigned int flags;
+ struct cmsghdr *cmsg;
+ unsigned int flags;
{
- struct ip6_rthdr *rthdr;
-
- rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
-
- switch(rthdr->ip6r_type) {
- case IPV6_RTHDR_TYPE_0:
- {
- struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
- if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT) {
-#ifdef DEBUG
- fprintf(stderr, "inet6_rthdr_lasthop: unsupported flag(%d)\n", flags);
-#endif
- return(-1);
- }
- if (rt0->ip6r0_segleft > 23) {
-#ifdef DEBUG
- fprintf(stderr, "inet6_rthdr_add: segment overflow\n");
-#endif
- return(-1);
- }
- if (flags == IPV6_RTHDR_STRICT) {
- int c, b;
- c = rt0->ip6r0_segleft / 8;
- b = rt0->ip6r0_segleft % 8;
- rt0->ip6r0_slmap[c] |= (1 << (7 - b));
- }
- break;
- }
- default:
-#ifdef DEBUG
- fprintf(stderr, "inet6_rthdr_lasthop: unknown type(%d)\n",
- rthdr->ip6r_type);
-#endif
- return(-1);
- }
+ struct ip6_rthdr *rthdr;
+
+ rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
+
+ switch (rthdr->ip6r_type) {
+ case IPV6_RTHDR_TYPE_0:
+ {
+ struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
+#ifdef COMPAT_RFC1883 /* XXX */
+ if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT)
+ return (-1);
+#endif /* COMPAT_RFC1883 */
+ if (rt0->ip6r0_segleft > 23)
+ return (-1);
+#ifdef COMPAT_RFC1883 /* XXX */
+ if (flags == IPV6_RTHDR_STRICT) {
+ int c, b;
+ c = rt0->ip6r0_segleft / 8;
+ b = rt0->ip6r0_segleft % 8;
+ rt0->ip6r0_slmap[c] |= (1 << (7 - b));
+ }
+#else
+ if (flags != IPV6_RTHDR_LOOSE)
+ return (-1);
+#endif /* COMPAT_RFC1883 */
+ break;
+ }
+ default:
+ return (-1);
+ }
- return(0);
+ return (0);
}
#if 0
int
inet6_rthdr_reverse(in, out)
- const struct cmsghdr *in;
- struct cmsghdr *out;
+ const struct cmsghdr *in;
+ struct cmsghdr *out;
{
-#ifdef DEBUG
- fprintf(stderr, "inet6_rthdr_reverse: not implemented yet\n");
-#endif
- return -1;
+
+ return (-1);
}
#endif
int
inet6_rthdr_segments(cmsg)
- const struct cmsghdr *cmsg;
+ const struct cmsghdr *cmsg;
{
- struct ip6_rthdr *rthdr;
+ struct ip6_rthdr *rthdr;
- rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
+ rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
- switch(rthdr->ip6r_type) {
- case IPV6_RTHDR_TYPE_0:
- {
- struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
+ switch (rthdr->ip6r_type) {
+ case IPV6_RTHDR_TYPE_0:
+ {
+ struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
- if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len) {
-#ifdef DEBUG
- fprintf(stderr, "inet6_rthdr_segments: invalid size(%d)\n",
- rt0->ip6r0_len);
-#endif
- return -1;
- }
+ if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len)
+ return (-1);
- return (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
- }
+ return (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
+ }
- default:
-#ifdef DEBUG
- fprintf(stderr, "inet6_rthdr_segments: unknown type(%d)\n",
- rthdr->ip6r_type);
-#endif
- return -1;
- }
+ default:
+ return (-1);
+ }
}
struct in6_addr *
inet6_rthdr_getaddr(cmsg, idx)
- struct cmsghdr *cmsg;
- int idx;
+ struct cmsghdr *cmsg;
+ int idx;
{
- struct ip6_rthdr *rthdr;
+ struct ip6_rthdr *rthdr;
+
+ rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
+
+ switch (rthdr->ip6r_type) {
+ case IPV6_RTHDR_TYPE_0:
+ {
+ struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
+ int naddr;
+
+ if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len)
+ return NULL;
+ naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
+ if (idx <= 0 || naddr < idx)
+ return NULL;
+#ifdef COMPAT_RFC2292
+ return (((struct in6_addr *)(rt0 + 1)) + idx - 1);
+#else
+ return (((struct in6_addr *)(rt0 + 1)) + idx);
+#endif
+ }
- rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
+ default:
+ return NULL;
+ }
+}
- switch(rthdr->ip6r_type) {
- case IPV6_RTHDR_TYPE_0:
- {
- struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
- int naddr;
+int
+inet6_rthdr_getflags(cmsg, idx)
+ const struct cmsghdr *cmsg;
+ int idx;
+{
+ struct ip6_rthdr *rthdr;
+
+ rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
+
+ switch (rthdr->ip6r_type) {
+ case IPV6_RTHDR_TYPE_0:
+ {
+ struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
+ int naddr;
+
+ if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len)
+ return (-1);
+ naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
+ if (idx < 0 || naddr < idx)
+ return (-1);
+#ifdef COMPAT_RFC1883 /* XXX */
+ if (rt0->ip6r0_slmap[idx / 8] & (0x80 >> (idx % 8)))
+ return IPV6_RTHDR_STRICT;
+ else
+ return IPV6_RTHDR_LOOSE;
+#else
+ return IPV6_RTHDR_LOOSE;
+#endif /* COMPAT_RFC1883 */
+ }
- if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len) {
-#ifdef DEBUG
- fprintf(stderr, "inet6_rthdr_getaddr: invalid size(%d)\n",
- rt0->ip6r0_len);
-#endif
- return NULL;
+ default:
+ return (-1);
}
- naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
- if (idx <= 0 || naddr < idx) {
-#ifdef DEBUG
- fprintf(stderr, "inet6_rthdr_getaddr: invalid idx(%d)\n", idx);
-#endif
- return NULL;
+}
+
+/*
+ * RFC3542 (2292bis) API
+ */
+
+socklen_t
+inet6_rth_space(int type, int segments)
+{
+ switch (type) {
+ case IPV6_RTHDR_TYPE_0:
+ return (((segments * 2) + 1) << 3);
+ default:
+ return (0); /* type not suppported */
}
- return &rt0->ip6r0_addr[idx - 1];
- }
+}
- default:
-#ifdef DEBUG
- fprintf(stderr, "inet6_rthdr_getaddr: unknown type(%d)\n",
- rthdr->ip6r_type);
-#endif
- return NULL;
- }
+void *
+inet6_rth_init(void *bp, socklen_t bp_len, int type, int segments)
+{
+ struct ip6_rthdr *rth = (struct ip6_rthdr *)bp;
+ struct ip6_rthdr0 *rth0;
+
+ switch (type) {
+ case IPV6_RTHDR_TYPE_0:
+ /* length validation */
+ if (bp_len < inet6_rth_space(IPV6_RTHDR_TYPE_0, segments))
+ return (NULL);
+
+ memset(bp, 0, bp_len);
+ rth0 = (struct ip6_rthdr0 *)rth;
+ rth0->ip6r0_len = segments * 2;
+ rth0->ip6r0_type = IPV6_RTHDR_TYPE_0;
+ rth0->ip6r0_segleft = 0;
+ rth0->ip6r0_reserved = 0;
+ break;
+ default:
+ return (NULL); /* type not supported */
+ }
+
+ return (bp);
}
int
-inet6_rthdr_getflags(cmsg, idx)
- const struct cmsghdr *cmsg;
- int idx;
+inet6_rth_add(void *bp, const struct in6_addr *addr)
{
- struct ip6_rthdr *rthdr;
+ struct ip6_rthdr *rth = (struct ip6_rthdr *)bp;
+ struct ip6_rthdr0 *rth0;
+ struct in6_addr *nextaddr;
+
+ switch (rth->ip6r_type) {
+ case IPV6_RTHDR_TYPE_0:
+ rth0 = (struct ip6_rthdr0 *)rth;
+ nextaddr = (struct in6_addr *)(rth0 + 1) + rth0->ip6r0_segleft;
+ *nextaddr = *addr;
+ rth0->ip6r0_segleft++;
+ break;
+ default:
+ return (-1); /* type not supported */
+ }
+
+ return (0);
+}
- rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
+int
+inet6_rth_reverse(const void *in, void *out)
+{
+ struct ip6_rthdr *rth_in = (struct ip6_rthdr *)in;
+ struct ip6_rthdr0 *rth0_in, *rth0_out;
+ int i, segments;
+
+ switch (rth_in->ip6r_type) {
+ case IPV6_RTHDR_TYPE_0:
+ rth0_in = (struct ip6_rthdr0 *)in;
+ rth0_out = (struct ip6_rthdr0 *)out;
+
+ /* parameter validation XXX too paranoid? */
+ if (rth0_in->ip6r0_len % 2)
+ return (-1);
+ segments = rth0_in->ip6r0_len / 2;
+
+ /* we can't use memcpy here, since in and out may overlap */
+ memmove((void *)rth0_out, (void *)rth0_in,
+ ((rth0_in->ip6r0_len) + 1) << 3);
+ rth0_out->ip6r0_segleft = segments;
+
+ /* reverse the addresses */
+ for (i = 0; i < segments / 2; i++) {
+ struct in6_addr addr_tmp, *addr1, *addr2;
+
+ addr1 = (struct in6_addr *)(rth0_out + 1) + i;
+ addr2 = (struct in6_addr *)(rth0_out + 1) +
+ (segments - i - 1);
+ addr_tmp = *addr1;
+ *addr1 = *addr2;
+ *addr2 = addr_tmp;
+ }
+
+ break;
+ default:
+ return (-1); /* type not supported */
+ }
- switch(rthdr->ip6r_type) {
- case IPV6_RTHDR_TYPE_0:
- {
- struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
- int naddr;
+ return (0);
+}
- if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len) {
-#ifdef DEBUG
- fprintf(stderr, "inet6_rthdr_getflags: invalid size(%d)\n",
- rt0->ip6r0_len);
-#endif
- return -1;
+int
+inet6_rth_segments(const void *bp)
+{
+ struct ip6_rthdr *rh = (struct ip6_rthdr *)bp;
+ struct ip6_rthdr0 *rh0;
+ int addrs;
+
+ switch (rh->ip6r_type) {
+ case IPV6_RTHDR_TYPE_0:
+ rh0 = (struct ip6_rthdr0 *)bp;
+
+ /*
+ * Validation for a type-0 routing header.
+ * Is this too strict?
+ */
+ if ((rh0->ip6r0_len % 2) != 0 ||
+ (addrs = (rh0->ip6r0_len >> 1)) < rh0->ip6r0_segleft)
+ return (-1);
+
+ return (addrs);
+ default:
+ return (-1); /* unknown type */
}
- naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
- if (idx < 0 || naddr < idx) {
-#ifdef DEBUG
- fprintf(stderr, "inet6_rthdr_getflags: invalid idx(%d)\n", idx);
-#endif
- return -1;
+}
+
+struct in6_addr *
+inet6_rth_getaddr(const void *bp, int idx)
+{
+ struct ip6_rthdr *rh = (struct ip6_rthdr *)bp;
+ struct ip6_rthdr0 *rh0;
+ int rthlen, addrs;
+
+ switch (rh->ip6r_type) {
+ case IPV6_RTHDR_TYPE_0:
+ rh0 = (struct ip6_rthdr0 *)bp;
+ rthlen = (rh0->ip6r0_len + 1) << 3;
+
+ /*
+ * Validation for a type-0 routing header.
+ * Is this too strict?
+ */
+ if ((rthlen % 2) != 0 ||
+ (addrs = (rthlen >> 1)) < rh0->ip6r0_segleft)
+ return (NULL);
+
+ if (idx < 0 || addrs <= idx)
+ return (NULL);
+
+ return (((struct in6_addr *)(rh0 + 1)) + idx);
+ default:
+ return (NULL); /* unknown type */
+ break;
}
- if (rt0->ip6r0_slmap[idx / 8] & (0x80 >> (idx % 8)))
- return IPV6_RTHDR_STRICT;
- else
- return IPV6_RTHDR_LOOSE;
- }
-
- default:
-#ifdef DEBUG
- fprintf(stderr, "inet6_rthdr_getflags: unknown type(%d)\n",
- rthdr->ip6r_type);
-#endif
- return -1;
- }
}
diff --git a/lib/libsdp/search.c b/lib/libsdp/search.c
index ab495e3..c739824 100644
--- a/lib/libsdp/search.c
+++ b/lib/libsdp/search.c
@@ -29,6 +29,7 @@
* $FreeBSD$
*/
+#include <sys/types.h>
#include <sys/uio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
diff --git a/sbin/ping6/Makefile b/sbin/ping6/Makefile
index 98378fc..4073a56 100644
--- a/sbin/ping6/Makefile
+++ b/sbin/ping6/Makefile
@@ -3,7 +3,8 @@
PROG= ping6
MAN= ping6.8
-CFLAGS+=-DINET6 -DIPSEC
+CFLAGS+=-DINET6 -DIPSEC -DKAME_SCOPEID -DUSE_RFC2292BIS \
+ -DHAVE_POLL_H -DHAVE_ARC4RANDOM
WARNS= 0
BINOWN= root
@@ -12,7 +13,4 @@ BINMODE=4555
LDADD= -lipsec -lm -lmd
DPADD= ${LIBIPSEC} ${LIBM} ${LIBMD}
-# kame scopeid hack
-CFLAGS+=-DKAME_SCOPEID
-
.include <bsd.prog.mk>
diff --git a/sbin/ping6/ping6.8 b/sbin/ping6/ping6.8
index 554bf2b..84a51ed 100644
--- a/sbin/ping6/ping6.8
+++ b/sbin/ping6/ping6.8
@@ -1,4 +1,4 @@
-.\" $KAME: ping6.8,v 1.43 2001/06/28 06:54:29 suz Exp $
+.\" $KAME: ping6.8,v 1.58 2003/06/20 12:00:22 itojun Exp $
.\"
.\" Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
.\" All rights reserved.
@@ -40,9 +40,9 @@ packets to network hosts
.Sh SYNOPSIS
.Nm
.\" without ipsec, or new ipsec
-.Op Fl dfHnNqRtvwW
+.Op Fl dfHmnNqtvwW
.\" old ipsec
-.\" .Op Fl AdEfnNqRtvwW
+.\" .Op Fl AdEfmnNqRtvwW
.Bk -words
.Op Fl a Ar addrtype
.Ek
@@ -53,6 +53,9 @@ packets to network hosts
.Op Fl c Ar count
.Ek
.Bk -words
+.Op Fl g Ar gateway
+.Ek
+.Bk -words
.Op Fl h Ar hoplimit
.Ek
.Bk -words
@@ -78,7 +81,7 @@ packets to network hosts
.Op Fl s Ar packetsize
.Ek
.Bk -words
-.Op Ar hops...\&
+.Op Ar hops ...
.Ek
.Bk -words
.Ar host
@@ -86,7 +89,7 @@ packets to network hosts
.Sh DESCRIPTION
The
.Nm
-utility uses the
+command uses the
.Tn ICMPv6
protocol's mandatory
.Tn ICMP6_ECHO_REQUEST
@@ -103,14 +106,14 @@ The options are as follows:
.\" old ipsec
.\" .It Fl A
.\" Enables transport-mode IPsec authentication header
-.\" (experimental).
+.\" .Pq 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 characters.
.Bl -tag -width Ds -compact
.It Ic a
-requests all the responder's unicast addresses.
+requests unicast addresses from all of the responder's interfaces.
If the character is omitted,
only those addresses which belong to the interface which has the
responder's address are requests.
@@ -134,7 +137,7 @@ This is an experimental option.
Set socket buffer size.
.It Fl c Ar count
Stop after sending
-(and receiving)
+.Pq and receiving
.Ar count
.Tn ECHO_RESPONSE
packets.
@@ -144,7 +147,7 @@ Set the
option on the socket being used.
.\" .It Fl E
.\" Enables transport-mode IPsec encapsulated security payload
-.\" (experimental).
+.\" .Pq experimental .
.It Fl f
Flood ping.
Outputs packets as fast as they come back or one hundred times per second,
@@ -152,7 +155,7 @@ whichever is more.
For every
.Tn ECHO_REQUEST
sent a period
-.Dq .\&
+.Dq \&.
is printed, while for every
.Tn ECHO_REPLY
received a backspace is printed.
@@ -161,11 +164,16 @@ 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 g Ar gateway
+Specifies to use
+.Ar gateway
+as the next hop to the destination.
+The gateway must be a neighbor of the sending node.
.It Fl H
Specifies to try reverse-lookup of IPv6 addresses.
The
.Nm
-utility does not try reverse-lookup unless the option is specified.
+command does not try reverse-lookup unless the option is specified.
.It Fl h Ar hoplimit
Set the IPv6 hoplimit.
.It Fl I Ar interface
@@ -189,6 +197,16 @@ is specified,
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 m
+By default,
+.Nm
+asks the kernel to fragment packets to fit into the minimum IPv6 MTU.
+.Fl m
+will suppress the behavior in the following two levels:
+when the option is specified once, the behavior will be disabled for
+unicast packets.
+When the option is more than once, it will be disabled for both
+unicast and multicast packets.
.It Fl n
Numeric output only.
No attempt will be made to lookup symbolic names from addresses in the reply.
@@ -197,12 +215,12 @@ Probe node information multicast group
.Pq Li ff02::2:xxxx:xxxx .
.Ar host
must be string hostname of the target
-(must not be a numeric IPv6 address).
+.Pq must not be a numeric IPv6 address .
Node information multicast group will be computed based on given
.Ar host ,
and will be used as the final destination.
Since node information multicast group is a link-local multicast group,
-destination link needs to be specified by
+outgoing interface needs to be specified by
.Fl I
option.
.It Fl p Ar pattern
@@ -222,26 +240,10 @@ specifies IPsec policy to be used for the probe.
Quiet output.
Nothing is displayed except the summary lines at startup time and
when finished.
-.It Fl R
-Make the kernel believe that the target
-.Ar host
-(or the first
-.Ar hop
-if you specify
-.Ar hops )
-is reachable, by injecting upper-layer reachability confirmation hint.
-The option is meaningful only if the target
-.Ar host
-(or the first hop)
-is a neighbor.
.It Fl S Ar sourceaddr
Specifies the source address of request packets.
-The source address must be one of the unicast addresses of the sending node.
-If the outgoing interface is specified by the
-.Fl I
-option as well,
-.Ar sourceaddr
-needs to be an address assigned to the specified interface.
+The source address must be one of the unicast addresses of the sending node,
+and must be numeric.
.It Fl s Ar packetsize
Specifies the number of data bytes to be sent.
The default is 56, which translates into 64
@@ -299,26 +301,13 @@ 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
-(and received)
+.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, mean, maximum, and standard deviation of
+received, and the minimum, maximum, mean, and standard deviation of
the round-trip times.
.Pp
-If
-.Nm
-receives a
-.Dv SIGINFO
-(see the
-.Cm status
-argument for
-.Xr stty 1 )
-signal, the current number of packets sent and received, and the
-minimum, mean, maximum, and standard deviation of the round-trip times
-will be written to the standard output in the same format as the
-standard completion message.
-.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
@@ -335,14 +324,12 @@ during normal operations or from automated scripts.
.\" When a
.\" .Ar packetsize
.\" is given, this indicated the size of this extra piece of data
-.\" (the default is 56).
+.\" .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
-.\" (the
-.\" .Tn ICMP
-.\" header).
+.\" .Pq the Tn ICMP header .
.\" .Pp
.\" If the data space is at least eight bytes large,
.\" .Nm
@@ -353,12 +340,12 @@ during normal operations or from automated scripts.
.Sh DUPLICATE AND DAMAGED PACKETS
The
.Nm
-utility will report duplicate and damaged packets.
+command 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
-(if ever)
+.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,
@@ -369,7 +356,7 @@ Damaged packets are obviously serious cause for alarm and often
indicate broken hardware somewhere in the
.Nm
packet's path
-(in the network or in the hosts).
+.Pq in the network or in the hosts .
.Sh TRYING DIFFERENT DATA PATTERNS
The
(inter)network
@@ -398,11 +385,10 @@ You can then examine this file for repeated patterns that you can test
using the
.Fl p
option of
-.Nm .
+.Nm Ns .
.Sh RETURN VALUES
-The
.Nm
-utility returns 0 on success (the host is alive),
+command returns 0 on success (the host is alive),
and non-zero if the arguments are incorrect or the host is not responding.
.Sh EXAMPLES
Normally,
@@ -451,11 +437,28 @@ ping6 -a agl dst.foo.com
.Rs
.%A Matt Crawford
.%T "IPv6 Node Information Queries"
-.%N draft-ietf-ipngwg-icmp-name-lookups-07.txt
-.%D August 2000
+.%N draft-ietf-ipngwg-icmp-name-lookups-09.txt
+.%D May 2002
.%O work in progress material
.Re
+.Sh HISTORY
+The
+.Xr ping 8
+command appeared in
+.Bx 4.3 .
+The
+.Nm
+command with IPv6 support first appeared in the WIDE Hydrangea IPv6
+protocol stack kit.
+.Pp
+IPv6 and IPsec support based on the KAME Project (http://www.kame.net/) stack
+was initially integrated into
+.Fx 4.0
.Sh BUGS
+.Nm
+is intentionally separate from
+.Xr ping 8 .
+.Pp
There have been many discussions on why we separate
.Nm
and
@@ -484,16 +487,3 @@ or
.Fl 4
option (or something like those) to specify the particular address family.
This essentially means that we have two different commands.
-.Sh HISTORY
-The
-.Xr ping 8
-command appeared in
-.Bx 4.3 .
-The
-.Nm
-utility with IPv6 support first appeared in WIDE Hydrangea IPv6 protocol stack
-kit.
-.Pp
-IPv6 and IPsec support based on the KAME Project (http://www.kame.net/) stack
-was initially integrated into
-.Fx 4.0
diff --git a/sbin/ping6/ping6.c b/sbin/ping6/ping6.c
index 70e2221..42c517c 100644
--- a/sbin/ping6/ping6.c
+++ b/sbin/ping6/ping6.c
@@ -1,4 +1,4 @@
-/* $KAME: ping6.c,v 1.126 2001/05/17 03:39:08 itojun Exp $ */
+/* $KAME: ping6.c,v 1.169 2003/07/25 06:01:47 itojun Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -123,30 +123,32 @@ static const char rcsid[] =
#include <err.h>
#include <errno.h>
#include <fcntl.h>
-#if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__)
#include <math.h>
-#endif
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#ifdef HAVE_POLL_H
+#include <poll.h>
+#endif
#ifdef IPSEC
#include <netinet6/ah.h>
#include <netinet6/ipsec.h>
#endif
-#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)
#include <md5.h>
-#else
-#include "md5.h"
-#endif
+
+struct tv32 {
+ u_int32_t tv32_sec;
+ u_int32_t tv32_usec;
+};
#define MAXPACKETLEN 131072
#define IP6LEN 40
-#define ICMP6ECHOLEN 8 /* icmp echo header length excluding time */
-#define ICMP6ECHOTMLEN sizeof(struct timeval)
+#define ICMP6ECHOLEN 8 /* icmp echo header len excluding time */
+#define ICMP6ECHOTMLEN sizeof(struct tv32)
#define ICMP6_NIQLEN (ICMP6ECHOLEN + 8)
/* FQDN case, 64 bits of nonce + 32 bits ttl */
#define ICMP6_NIRLEN (ICMP6ECHOLEN + 12)
@@ -180,9 +182,6 @@ static const char rcsid[] =
#define F_FQDN 0x1000
#define F_INTERFACE 0x2000
#define F_SRCADDR 0x4000
-#ifdef IPV6_REACHCONF
-#define F_REACHCONF 0x8000
-#endif
#define F_HOSTNAME 0x10000
#define F_FQDNOLD 0x20000
#define F_NIGROUP 0x40000
@@ -209,6 +208,7 @@ 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 */
+socklen_t srclen;
int datalen = DEFDATALEN;
int s; /* socket file descriptor */
u_char outpack[MAXPACKETLEN];
@@ -217,7 +217,6 @@ char DOT = '.';
char *hostname;
int ident; /* process id to identify our packets */
u_int8_t nonce[8]; /* nonce field for node information */
-struct in6_addr srcaddr;
int hoplimit = -1; /* hoplimit */
int pathmtu = 0; /* path MTU for the destination. 0 = unspec. */
@@ -233,9 +232,7 @@ 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 */
-#if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__)
double tsumsq = 0.0; /* sum of all times squared, for std. dev. */
-#endif
/* for node addresses */
u_short naflags;
@@ -245,17 +242,16 @@ struct msghdr smsghdr;
struct iovec smsgiov;
char *scmsg = 0;
-volatile int signo;
volatile sig_atomic_t seenalrm;
volatile sig_atomic_t seenint;
#ifdef SIGINFO
volatile sig_atomic_t seeninfo;
#endif
+int main(int, char *[]);
void fill(char *, char *);
int get_hoplim(struct msghdr *);
int get_pathmtu(struct msghdr *);
-void set_pathmtu(int);
struct in6_pktinfo *get_rcvpktinfo(struct msghdr *);
void onsignal(int);
void retransmit(void);
@@ -270,7 +266,7 @@ void pr_nodeaddr(struct icmp6_nodeinfo *, int);
int myechoreply(const struct icmp6_hdr *);
int mynireply(const struct icmp6_nodeinfo *);
char *dnsdecode(const u_char **, const u_char *, const u_char *,
- u_char *, size_t);
+ char *, size_t);
void pr_pack(u_char *, int, struct msghdr *);
void pr_exthdrs(struct msghdr *);
void pr_ip6opt(void *);
@@ -290,17 +286,31 @@ main(argc, argv)
{
struct itimerval itimer;
struct sockaddr_in6 from;
+#ifndef HAVE_ARC4RANDOM
+ struct timeval seed;
+#endif
+#ifdef HAVE_POLL_H
+ int timeout;
+#else
struct timeval timeout, *tv;
+#endif
struct addrinfo hints;
+#ifdef HAVE_POLL_H
+ struct pollfd fdmaskp[1];
+#else
fd_set *fdmaskp;
int fdmasks;
+#endif
int cc, i;
- int ch, fromlen, hold, packlen, preload, optval, ret_ga;
+ int ch, hold, packlen, preload, optval, ret_ga;
u_char *datap, *packet;
- char *e, *target, *ifname = NULL;
+ char *e, *target, *ifname = NULL, *gateway = NULL;
int ip6optlen = 0;
struct cmsghdr *scmsgp = NULL;
+#if defined(SO_SNDBUF) && defined(SO_RCVBUF)
+ u_long lsockbufsize;
int sockbufsize = 0;
+#endif
int usepktinfo = 0;
struct in6_pktinfo *pktinfo = NULL;
#ifdef USE_RFC2292BIS
@@ -312,6 +322,9 @@ main(argc, argv)
#endif
double intval;
size_t rthlen;
+#ifdef IPV6_USE_MIN_MTU
+ int mflag = 0;
+#endif
/* just to be sure */
memset(&smsghdr, 0, sizeof(smsghdr));
@@ -329,7 +342,7 @@ main(argc, argv)
#endif /*IPSEC_POLICY_IPSEC*/
#endif
while ((ch = getopt(argc, argv,
- "a:b:c:dfHh:I:i:l:mnNp:qRS:s:tvwW" ADDOPTS)) != -1) {
+ "a:b:c:dfHg:h:I:i:l:mnNp:qS:s:tvwW" ADDOPTS)) != -1) {
#undef ADDOPTS
switch (ch) {
case 'a':
@@ -377,7 +390,13 @@ main(argc, argv)
}
case 'b':
#if defined(SO_SNDBUF) && defined(SO_RCVBUF)
- sockbufsize = atoi(optarg);
+ errno = 0;
+ e = NULL;
+ lsockbufsize = strtoul(optarg, &e, 10);
+ sockbufsize = lsockbufsize;
+ if (errno || !*optarg || *e ||
+ sockbufsize != lsockbufsize)
+ errx(1, "invalid socket buffer size");
#else
errx(1,
"-b option ignored: SO_SNDBUF/SO_RCVBUF socket options not supported");
@@ -393,16 +412,23 @@ main(argc, argv)
options |= F_SO_DEBUG;
break;
case 'f':
- if (getuid())
- errx(1, "must be superuser to flood ping");
+ if (getuid()) {
+ errno = EPERM;
+ errx(1, "Must be superuser to flood ping");
+ }
options |= F_FLOOD;
setbuf(stdout, (char *)NULL);
break;
+ case 'g':
+ gateway = optarg;
+ break;
case 'H':
options |= F_HOSTNAME;
break;
case 'h': /* hoplimit */
hoplimit = strtol(optarg, &e, 10);
+ if (*optarg == '\0' || *e != '\0')
+ errx(1, "illegal hoplimit %s", optarg);
if (255 < hoplimit || hoplimit < -1)
errx(1,
"illegal hoplimit -- %s", optarg);
@@ -435,15 +461,17 @@ main(argc, argv)
options |= F_INTERVAL;
break;
case 'l':
- if (getuid())
- errx(1, "must be superuser to preload");
+ if (getuid()) {
+ errno = EPERM;
+ errx(1, "Must be superuser to preload");
+ }
preload = strtol(optarg, &e, 10);
if (preload < 0 || *optarg == '\0' || *e != '\0')
errx(1, "illegal preload value -- %s", optarg);
break;
case 'm':
#ifdef IPV6_USE_MIN_MTU
- options |= F_NOMINMTU;
+ mflag++;
break;
#else
errx(1, "-%c is not supported on this platform", ch);
@@ -462,19 +490,26 @@ main(argc, argv)
case 'q':
options |= F_QUIET;
break;
- case 'R':
-#ifdef IPV6_REACHCONF
- options |= F_REACHCONF;
- break;
-#else
- errx(1, "-R is not supported in this configuration");
-#endif
case 'S':
- /* XXX: use getaddrinfo? */
- if (inet_pton(AF_INET6, optarg, (void *)&srcaddr) != 1)
- errx(1, "invalid IPv6 address: %s", optarg);
+ memset(&hints, 0, sizeof(struct addrinfo));
+ hints.ai_flags = AI_NUMERICHOST; /* allow hostname? */
+ hints.ai_family = AF_INET6;
+ hints.ai_socktype = SOCK_RAW;
+ hints.ai_protocol = IPPROTO_ICMPV6;
+
+ ret_ga = getaddrinfo(optarg, NULL, &hints, &res);
+ if (ret_ga) {
+ errx(1, "invalid source address: %s",
+ gai_strerror(ret_ga));
+ }
+ /*
+ * res->ai_family must be AF_INET6 and res->ai_addrlen
+ * must be sizeof(src).
+ */
+ memcpy(&src, res->ai_addr, res->ai_addrlen);
+ srclen = res->ai_addrlen;
+ freeaddrinfo(res);
options |= F_SRCADDR;
- usepktinfo++;
break;
case 's': /* size of packet to send */
datalen = strtol(optarg, &e, 10);
@@ -528,6 +563,7 @@ main(argc, argv)
/*NOTREACHED*/
}
}
+
argc -= optind;
argv += optind;
@@ -560,7 +596,7 @@ main(argc, argv)
target = argv[argc - 1];
/* getaddrinfo */
- bzero(&hints, sizeof(struct addrinfo));
+ memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_flags = AI_CANONNAME;
hints.ai_family = AF_INET6;
hints.ai_socktype = SOCK_RAW;
@@ -583,6 +619,38 @@ main(argc, argv)
res->ai_protocol)) < 0)
err(1, "socket");
+ /* set the source address if specified. */
+ if ((options & F_SRCADDR) &&
+ bind(s, (struct sockaddr *)&src, srclen) != 0) {
+ err(1, "bind");
+ }
+
+ /* set the gateway (next hop) if specified */
+ if (gateway) {
+ struct addrinfo ghints, *gres;
+ int error;
+
+ memset(&ghints, 0, sizeof(ghints));
+ ghints.ai_family = AF_INET6;
+ ghints.ai_socktype = SOCK_RAW;
+ ghints.ai_protocol = IPPROTO_ICMPV6;
+
+ error = getaddrinfo(gateway, NULL, &hints, &gres);
+ if (error) {
+ errx(1, "getaddrinfo for the gateway %s: %s",
+ gateway, gai_strerror(error));
+ }
+ if (gres->ai_next && (options & F_VERBOSE))
+ warnx("gateway resolves to multiple addresses");
+
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_NEXTHOP,
+ gres->ai_addr, gres->ai_addrlen)) {
+ err(1, "setsockopt(IPV6_NEXTHOP)");
+ }
+
+ freeaddrinfo(gres);
+ }
+
/*
* let the kerel pass extension headers of incoming packets,
* for privileged socket options
@@ -619,11 +687,11 @@ main(argc, argv)
seteuid(getuid());
setuid(getuid());
- if (options & F_FLOOD && options & F_INTERVAL)
+ if ((options & F_FLOOD) && (options & F_INTERVAL))
errx(1, "-f and -i incompatible options");
if ((options & F_NOUSERDATA) == 0) {
- if (datalen >= sizeof(struct timeval)) {
+ if (datalen >= sizeof(struct tv32)) {
/* we can time transfer */
timing = 1;
} else
@@ -641,15 +709,15 @@ main(argc, argv)
}
if (!(packet = (u_char *)malloc((u_int)packlen)))
- errx(1, "unable to allocate packet");
+ err(1, "Unable to allocate packet");
if (!(options & F_PINGFILLED))
for (i = ICMP6ECHOLEN; i < packlen; ++i)
*datap++ = i;
ident = getpid() & 0xFFFF;
-#ifndef __OpenBSD__
- gettimeofday(&timeout, NULL);
- srand((unsigned int)(timeout.tv_sec ^ timeout.tv_usec ^ (long)ident));
+#ifndef HAVE_ARC4RANDOM
+ gettimeofday(&seed, NULL);
+ srand((unsigned int)(seed.tv_sec ^ seed.tv_usec ^ (long)ident));
memset(nonce, 0, sizeof(nonce));
for (i = 0; i < sizeof(nonce); i += sizeof(int))
*((int *)&nonce[i]) = rand();
@@ -670,8 +738,9 @@ main(argc, argv)
&optval, sizeof(optval)) == -1)
err(1, "IPV6_MULTICAST_HOPS");
#ifdef IPV6_USE_MIN_MTU
- if ((options & F_NOMINMTU) == 0) {
- optval = 1;
+ if (mflag != 1) {
+ optval = mflag > 1 ? 0 : 1;
+
if (setsockopt(s, IPPROTO_IPV6, IPV6_USE_MIN_MTU,
&optval, sizeof(optval)) == -1)
err(1, "setsockopt(IPV6_USE_MIN_MTU)");
@@ -765,11 +834,6 @@ main(argc, argv)
if (hoplimit != -1)
ip6optlen += CMSG_SPACE(sizeof(int));
-#ifdef IPV6_REACHCONF
- if (options & F_REACHCONF)
- ip6optlen += CMSG_SPACE(0);
-#endif
-
/* set IP6 packet options */
if (ip6optlen) {
if ((scmsg = (char *)malloc(ip6optlen)) == 0)
@@ -798,10 +862,6 @@ main(argc, argv)
errx(1, "%s: invalid interface name", ifname);
#endif
}
- /* set the source address */
- if (options & F_SRCADDR)/* pktinfo must be valid */
- pktinfo->ipi6_addr = srcaddr;
-
if (hoplimit != -1) {
scmsgp->cmsg_len = CMSG_LEN(sizeof(int));
scmsgp->cmsg_level = IPPROTO_IPV6;
@@ -810,15 +870,6 @@ main(argc, argv)
scmsgp = CMSG_NXTHDR(&smsghdr, scmsgp);
}
-#ifdef IPV6_REACHCONF
- if (options & F_REACHCONF) {
- scmsgp->cmsg_len = CMSG_LEN(0);
- scmsgp->cmsg_level = IPPROTO_IPV6;
- scmsgp->cmsg_type = IPV6_REACHCONF;
-
- scmsgp = CMSG_NXTHDR(&smsghdr, scmsgp);
- }
-#endif
if (argc > 1) { /* some intermediate addrs are specified */
int hops, error;
@@ -873,11 +924,13 @@ main(argc, argv)
scmsgp = CMSG_NXTHDR(&smsghdr, scmsgp);
}
- {
+ if (!(options & F_SRCADDR)) {
/*
- * source selection
+ * get the source address. XXX since we revoked the root
+ * privilege, we cannot use a raw socket for this.
*/
- int dummy, len = sizeof(src);
+ int dummy;
+ socklen_t len = sizeof(src);
if ((dummy = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
err(1, "UDP socket");
@@ -894,9 +947,14 @@ main(argc, argv)
err(1, "UDP setsockopt(IPV6_PKTINFO)");
if (hoplimit != -1 &&
- setsockopt(dummy, IPPROTO_IPV6, IPV6_HOPLIMIT,
+ setsockopt(dummy, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
(void *)&hoplimit, sizeof(hoplimit)))
- err(1, "UDP setsockopt(IPV6_HOPLIMIT)");
+ err(1, "UDP setsockopt(IPV6_UNICAST_HOPS)");
+
+ if (hoplimit != -1 &&
+ setsockopt(dummy, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
+ (void *)&hoplimit, sizeof(hoplimit)))
+ err(1, "UDP setsockopt(IPV6_MULTICAST_HOPS)");
if (rthdr &&
setsockopt(dummy, IPPROTO_IPV6, IPV6_RTHDR,
@@ -984,15 +1042,17 @@ main(argc, argv)
itimer.it_interval = interval;
itimer.it_value = interval;
(void)setitimer(ITIMER_REAL, &itimer, NULL);
- if (ntransmitted == 0)
+ if (ntransmitted)
retransmit();
}
+#ifndef HAVE_POLL_H
fdmasks = howmany(s + 1, NFDBITS) * sizeof(fd_mask);
if ((fdmaskp = malloc(fdmasks)) == NULL)
err(1, "malloc");
+#endif
- signo = seenalrm = seenint = 0;
+ seenalrm = seenint = 0;
#ifdef SIGINFO
seeninfo = 0;
#endif
@@ -1024,24 +1084,42 @@ main(argc, argv)
if (options & F_FLOOD) {
(void)pinger();
+#ifdef HAVE_POLL_H
+ timeout = 10;
+#else
timeout.tv_sec = 0;
timeout.tv_usec = 10000;
tv = &timeout;
- } else
+#endif
+ } else {
+#ifdef HAVE_POLL_H
+ timeout = INFTIM;
+#else
tv = NULL;
+#endif
+ }
+#ifdef HAVE_POLL_H
+ fdmaskp[0].fd = s;
+ fdmaskp[0].events = POLLIN;
+ cc = poll(fdmaskp, 1, timeout);
+#else
memset(fdmaskp, 0, fdmasks);
FD_SET(s, fdmaskp);
cc = select(s + 1, fdmaskp, NULL, NULL, tv);
+#endif
if (cc < 0) {
if (errno != EINTR) {
+#ifdef HAVE_POLL_H
+ warn("poll");
+#else
warn("select");
+#endif
sleep(1);
}
continue;
} else if (cc == 0)
continue;
- fromlen = sizeof(from);
m.msg_name = (caddr_t)&from;
m.msg_namelen = sizeof(from);
memset(&iov, 0, sizeof(iov));
@@ -1073,7 +1151,6 @@ main(argc, argv)
printf("new path MTU (%d) is "
"notified\n", mtu);
}
- set_pathmtu(mtu);
}
continue;
} else {
@@ -1093,7 +1170,7 @@ void
onsignal(sig)
int sig;
{
- signo = sig;
+
switch (sig) {
case SIGALRM:
seenalrm++;
@@ -1245,9 +1322,14 @@ pinger()
icp->icmp6_code = 0;
icp->icmp6_id = htons(ident);
icp->icmp6_seq = ntohs(seq);
- if (timing)
- (void)gettimeofday((struct timeval *)
- &outpack[ICMP6ECHOLEN], NULL);
+ if (timing) {
+ struct timeval tv;
+ struct tv32 *tv32;
+ (void)gettimeofday(&tv, NULL);
+ tv32 = (struct tv32 *)&outpack[ICMP6ECHOLEN];
+ tv32->tv32_sec = htonl(tv.tv_sec);
+ tv32->tv32_usec = htonl(tv.tv_usec);
+ }
cc = ICMP6ECHOLEN + datalen;
}
@@ -1305,7 +1387,7 @@ dnsdecode(sp, ep, base, buf, bufsiz)
const u_char **sp;
const u_char *ep;
const u_char *base; /*base for compressed name*/
- u_char *buf;
+ char *buf;
size_t bufsiz;
{
int i;
@@ -1322,7 +1404,7 @@ dnsdecode(sp, ep, base, buf, bufsiz)
while (cp < ep) {
i = *cp;
if (i == 0 || cp != *sp) {
- if (strlcat(buf, ".", bufsiz) >= bufsiz)
+ if (strlcat((char *)buf, ".", bufsiz) >= bufsiz)
return NULL; /*result overrun*/
}
if (i == 0)
@@ -1347,7 +1429,7 @@ dnsdecode(sp, ep, base, buf, bufsiz)
while (i-- > 0 && cp < ep) {
l = snprintf(cresult, sizeof(cresult),
isprint(*cp) ? "%c" : "\\%03o", *cp & 0xff);
- if (l < 0 || l >= sizeof(cresult))
+ if (l >= sizeof(cresult) || l < 0)
return NULL;
if (strlcat(buf, cresult, bufsiz) >= bufsiz)
return NULL; /*result overrun*/
@@ -1385,7 +1467,8 @@ pr_pack(buf, cc, mhdr)
int fromlen;
u_char *cp = NULL, *dp, *end = buf + cc;
struct in6_pktinfo *pktinfo = NULL;
- struct timeval tv, *tp;
+ struct timeval tv, tp;
+ struct tv32 *tpp;
double triptime = 0;
int dupflag;
size_t off;
@@ -1399,7 +1482,7 @@ pr_pack(buf, cc, mhdr)
mhdr->msg_namelen != sizeof(struct sockaddr_in6) ||
((struct sockaddr *)mhdr->msg_name)->sa_family != AF_INET6) {
if (options & F_VERBOSE)
- warnx("invalid peername\n");
+ warnx("invalid peername");
return;
}
from = (struct sockaddr *)mhdr->msg_name;
@@ -1427,14 +1510,14 @@ pr_pack(buf, cc, mhdr)
seq = ntohs(icp->icmp6_seq);
++nreceived;
if (timing) {
- tp = (struct timeval *)(icp + 1);
- tvsub(&tv, tp);
+ tpp = (struct tv32 *)(icp + 1);
+ tp.tv_sec = ntohl(tpp->tv32_sec);
+ tp.tv_usec = ntohl(tpp->tv32_usec);
+ tvsub(&tv, &tp);
triptime = ((double)tv.tv_sec) * 1000.0 +
((double)tv.tv_usec) / 1000.0;
tsum += triptime;
-#if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__)
tsumsq += triptime * triptime;
-#endif
if (triptime < tmin)
tmin = triptime;
if (triptime > tmax)
@@ -1464,9 +1547,7 @@ pr_pack(buf, cc, mhdr)
memset(&dstsa, 0, sizeof(dstsa));
dstsa.sin6_family = AF_INET6;
-#ifdef SIN6_LEN
dstsa.sin6_len = sizeof(dstsa);
-#endif
dstsa.sin6_scope_id = pktinfo->ipi6_ifindex;
dstsa.sin6_addr = pktinfo->ipi6_addr;
(void)printf(" dst=%s",
@@ -1767,7 +1848,7 @@ pr_rthdr(void *extbuf)
else {
if (!inet_ntop(AF_INET6, in6, ntopbuf,
sizeof(ntopbuf)))
- strncpy(ntopbuf, "?", sizeof(ntopbuf));
+ strlcpy(ntopbuf, "?", sizeof(ntopbuf));
printf(" [%d]%s\n", i, ntopbuf);
}
}
@@ -1787,9 +1868,9 @@ pr_rthdr(void *extbuf)
#endif /* USE_RFC2292BIS */
int
-pr_bitrange(v, s, ii)
+pr_bitrange(v, soff, ii)
u_int32_t v;
- int s;
+ int soff;
int ii;
{
int off;
@@ -1800,7 +1881,7 @@ pr_bitrange(v, s, ii)
/* shift till we have 0x01 */
if ((v & 0x01) == 0) {
if (ii > 1)
- printf("-%u", s + off - 1);
+ printf("-%u", soff + off - 1);
ii = 0;
switch (v & 0x0f) {
case 0x00:
@@ -1828,7 +1909,7 @@ pr_bitrange(v, s, ii)
break;
}
if (!ii)
- printf(" %u", s + off);
+ printf(" %u", soff + off);
ii += i;
v >>= i; off += i;
}
@@ -1949,7 +2030,7 @@ pr_nodeaddr(ni, nilen)
if (inet_ntop(AF_INET6, cp, ntop_buf, sizeof(ntop_buf)) ==
NULL)
- strncpy(ntop_buf, "?", sizeof(ntop_buf));
+ strlcpy(ntop_buf, "?", sizeof(ntop_buf));
printf(" %s", ntop_buf);
if (withttl) {
if (ttl == 0xffffffff) {
@@ -2065,60 +2146,6 @@ get_pathmtu(mhdr)
return(0);
}
-void
-set_pathmtu(mtu)
- int mtu;
-{
-#ifdef IPV6_USE_MTU
- static int firsttime = 1;
- struct cmsghdr *cm;
-
- if (firsttime) {
- int oldlen = smsghdr.msg_controllen;
- char *oldbuf = smsghdr.msg_control;
-
- /* XXX: We need to enlarge control message buffer */
- firsttime = 0; /* prevent further enlargement */
-
- smsghdr.msg_controllen = oldlen + CMSG_SPACE(sizeof(int));
- if ((smsghdr.msg_control =
- (char *)malloc(smsghdr.msg_controllen)) == NULL)
- err(1, "set_pathmtu: malloc");
- cm = (struct cmsghdr *)CMSG_FIRSTHDR(&smsghdr);
- cm->cmsg_len = CMSG_LEN(sizeof(int));
- cm->cmsg_level = IPPROTO_IPV6;
- cm->cmsg_type = IPV6_USE_MTU;
-
- cm = (struct cmsghdr *)CMSG_NXTHDR(&smsghdr, cm);
- if (oldlen)
- memcpy((void *)cm, (void *)oldbuf, oldlen);
-
- free(oldbuf);
- }
-
- /*
- * look for a cmsgptr that points MTU structure.
- * XXX: this procedure seems redundant at this moment, but we'd better
- * keep the code generic enough for future extensions.
- */
- for (cm = CMSG_FIRSTHDR(&smsghdr); cm;
- cm = (struct cmsghdr *)CMSG_NXTHDR(&smsghdr, cm)) {
- if (cm->cmsg_len == 0) /* XXX: paranoid check */
- errx(1, "set_pathmtu: internal error");
-
- if (cm->cmsg_level == IPPROTO_IPV6 &&
- cm->cmsg_type == IPV6_USE_MTU &&
- cm->cmsg_len == CMSG_LEN(sizeof(int)))
- break;
- }
-
- if (cm == NULL)
- errx(1, "set_pathmtu: internal error: no space for path MTU");
-
- *(int *)CMSG_DATA(cm) = mtu;
-#endif
-}
-
/*
* tvsub --
* Subtract 2 timeval structs: out = out - in. Out is assumed to
@@ -2144,12 +2171,13 @@ void
onint(notused)
int notused;
{
- (void)signal(SIGINT, SIG_IGN);
- (void)signal(SIGALRM, SIG_IGN);
-
summary();
- exit(nreceived == 0);
+ (void)signal(SIGINT, SIG_DFL);
+ (void)kill(getpid(), SIGINT);
+
+ /* NOTREACHED */
+ exit(1);
}
/*
@@ -2167,10 +2195,10 @@ summary()
(void)printf("+%ld duplicates, ", nrepeats);
if (ntransmitted) {
if (nreceived > ntransmitted)
- (void)printf("-- somebody's printing up packets!");
+ (void)printf("-- somebody's duplicating packets!");
else
- (void)printf("%d%% packet loss",
- (int) (((ntransmitted - nreceived) * 100) /
+ (void)printf("%.1f%% packet loss",
+ ((((double)ntransmitted - nreceived) * 100.0) /
ntransmitted));
}
(void)putchar('\n');
@@ -2178,30 +2206,24 @@ summary()
/* Only display average to microseconds */
double num = nreceived + nrepeats;
double avg = tsum / num;
-#if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__)
double dev = sqrt(tsumsq / num - avg * avg);
(void)printf(
"round-trip min/avg/max/std-dev = %.3f/%.3f/%.3f/%.3f ms\n",
tmin, avg, tmax, dev);
-#else
- (void)printf(
- "round-trip min/avg/max = %.3f/%.3f/%.3f ms\n",
- tmin, avg, tmax);
-#endif
(void)fflush(stdout);
}
(void)fflush(stdout);
}
/*subject type*/
-static char *niqcode[] = {
+static const char *niqcode[] = {
"IPv6 address",
"DNS label", /*or empty*/
"IPv4 address",
};
/*result code*/
-static char *nircode[] = {
+static const char *nircode[] = {
"Success", "Refused", "Unknown",
};
@@ -2323,11 +2345,11 @@ pr_icmph(icp, end)
(void)printf("Redirect\n");
if (!inet_ntop(AF_INET6, &red->nd_rd_dst, ntop_buf,
sizeof(ntop_buf)))
- strncpy(ntop_buf, "?", sizeof(ntop_buf));
+ strlcpy(ntop_buf, "?", sizeof(ntop_buf));
(void)printf("Destination: %s", ntop_buf);
if (!inet_ntop(AF_INET6, &red->nd_rd_target, ntop_buf,
sizeof(ntop_buf)))
- strncpy(ntop_buf, "?", sizeof(ntop_buf));
+ strlcpy(ntop_buf, "?", sizeof(ntop_buf));
(void)printf(" New Target: %s", ntop_buf);
break;
case ICMP6_NI_QUERY:
@@ -2459,10 +2481,10 @@ pr_iph(ip6)
(ip6->ip6_vfc & IPV6_VERSION_MASK) >> 4, tc, (u_int32_t)ntohl(flow),
ntohs(ip6->ip6_plen), ip6->ip6_nxt, ip6->ip6_hlim);
if (!inet_ntop(AF_INET6, &ip6->ip6_src, ntop_buf, sizeof(ntop_buf)))
- strncpy(ntop_buf, "?", sizeof(ntop_buf));
+ strlcpy(ntop_buf, "?", sizeof(ntop_buf));
printf("%s->", ntop_buf);
if (!inet_ntop(AF_INET6, &ip6->ip6_dst, ntop_buf, sizeof(ntop_buf)))
- strncpy(ntop_buf, "?", sizeof(ntop_buf));
+ strlcpy(ntop_buf, "?", sizeof(ntop_buf));
printf("%s\n", ntop_buf);
}
@@ -2594,7 +2616,7 @@ fill(bp, patp)
/* xxx */
if (ii > 0)
for (kk = 0;
- kk <= MAXDATALEN - (8 + sizeof(struct timeval) + ii);
+ kk <= MAXDATALEN - (8 + sizeof(struct tv32) + ii);
kk += ii)
for (jj = 0; jj < ii; ++jj)
bp[jj + kk] = pat[jj];
@@ -2623,7 +2645,7 @@ setpolicy(so, policy)
errx(1, "%s", ipsec_strerror());
if (setsockopt(s, IPPROTO_IPV6, IPV6_IPSEC_POLICY, buf,
ipsec_get_policylen(buf)) < 0)
- warnx("unable to set IPSec policy");
+ warnx("Unable to set IPsec policy");
free(buf);
return 0;
@@ -2636,7 +2658,7 @@ nigroup(name)
char *name;
{
char *p;
- unsigned char *q;
+ char *q;
MD5_CTX ctxt;
u_int8_t digest[16];
u_int8_t c;
@@ -2654,16 +2676,16 @@ nigroup(name)
hbuf[(int)l] = '\0';
for (q = name; *q; q++) {
- if (isupper(*q))
- *q = tolower(*q);
+ if (isupper(*(unsigned char *)q))
+ *q = tolower(*(unsigned char *)q);
}
/* generate 8 bytes of pseudo-random value. */
- bzero(&ctxt, sizeof(ctxt));
+ memset(&ctxt, 0, sizeof(ctxt));
MD5Init(&ctxt);
c = l & 0xff;
MD5Update(&ctxt, &c, sizeof(c));
- MD5Update(&ctxt, name, l);
+ MD5Update(&ctxt, (unsigned char *)name, l);
MD5Final(digest, &ctxt);
if (inet_pton(AF_INET6, "ff02::2:0000:0000", &in6) != 1)
@@ -2685,9 +2707,6 @@ usage()
"m"
#endif
"nNqtvwW"
-#ifdef IPV6_REACHCONF
- "R"
-#endif
#ifdef IPSEC
#ifdef IPSEC_POLICY_IPSEC
"] [-P policy"
@@ -2695,9 +2714,9 @@ usage()
"AE"
#endif
#endif
- "] [-a [aAclsg]] [-b sockbufsiz] [-c count]\n"
+ "] [-a [aAclsg]] [-b sockbufsiz] [-c count] \n"
"\t[-I interface] [-i wait] [-l preload] [-p pattern] "
"[-S sourceaddr]\n"
- "\t[-s packetsize] [-h hoplimit] [hops...] host\n");
+ "\t[-s packetsize] [-h hoplimit] [hops...] [-g gateway] host\n");
exit(1);
}
diff --git a/sys/netinet/icmp6.h b/sys/netinet/icmp6.h
index 2d29006..804ba6c 100644
--- a/sys/netinet/icmp6.h
+++ b/sys/netinet/icmp6.h
@@ -80,7 +80,7 @@ struct icmp6_hdr {
u_int16_t icmp6_un_data16[2]; /* type-specific field */
u_int8_t icmp6_un_data8[4]; /* type-specific field */
} icmp6_dataun;
-} __packed;
+} __attribute__((__packed__));
#define icmp6_data32 icmp6_dataun.icmp6_un_data32
#define icmp6_data16 icmp6_dataun.icmp6_un_data16
@@ -98,13 +98,15 @@ struct icmp6_hdr {
#define ICMP6_ECHO_REQUEST 128 /* echo service */
#define ICMP6_ECHO_REPLY 129 /* echo reply */
-#define ICMP6_MEMBERSHIP_QUERY 130 /* group membership query */
#define MLD_LISTENER_QUERY 130 /* multicast listener query */
-#define ICMP6_MEMBERSHIP_REPORT 131 /* group membership report */
#define MLD_LISTENER_REPORT 131 /* multicast listener report */
-#define ICMP6_MEMBERSHIP_REDUCTION 132 /* group membership termination */
#define MLD_LISTENER_DONE 132 /* multicast listener done */
+/* RFC2292 decls */
+#define ICMP6_MEMBERSHIP_QUERY 130 /* group membership query */
+#define ICMP6_MEMBERSHIP_REPORT 131 /* group membership report */
+#define ICMP6_MEMBERSHIP_REDUCTION 132 /* group membership termination */
+
#ifndef _KERNEL
/* the followings are for backward compatibility to old KAME apps. */
#define MLD6_LISTENER_QUERY MLD_LISTENER_QUERY
@@ -131,15 +133,12 @@ struct icmp6_hdr {
#define MLD_MTRACE_RESP 200 /* mtrace resp (to sender) */
#define MLD_MTRACE 201 /* mtrace messages */
-#define ICMP6_HADISCOV_REQUEST 202 /* XXX To be defined */
-#define ICMP6_HADISCOV_REPLY 203 /* XXX To be defined */
-
#ifndef _KERNEL
#define MLD6_MTRACE_RESP MLD_MTRACE_RESP
#define MLD6_MTRACE MLD_MTRACE
#endif
-#define ICMP6_MAXTYPE 203
+#define ICMP6_MAXTYPE 201
#define ICMP6_DST_UNREACH_NOROUTE 0 /* no route to destination */
#define ICMP6_DST_UNREACH_ADMIN 1 /* administratively prohibited */
@@ -179,7 +178,7 @@ struct icmp6_hdr {
struct mld_hdr {
struct icmp6_hdr mld_icmp6_hdr;
struct in6_addr mld_addr; /* multicast address */
-} __packed;
+} __attribute__((__packed__));
/* definitions to provide backward compatibility to old KAME applications */
#ifndef _KERNEL
@@ -206,7 +205,7 @@ struct mld_hdr {
struct nd_router_solicit { /* router solicitation */
struct icmp6_hdr nd_rs_hdr;
/* could be followed by options */
-} __packed;
+} __attribute__((__packed__));
#define nd_rs_type nd_rs_hdr.icmp6_type
#define nd_rs_code nd_rs_hdr.icmp6_code
@@ -218,7 +217,7 @@ struct nd_router_advert { /* router advertisement */
u_int32_t nd_ra_reachable; /* reachable time */
u_int32_t nd_ra_retransmit; /* retransmit timer */
/* could be followed by options */
-} __packed;
+} __attribute__((__packed__));
#define nd_ra_type nd_ra_hdr.icmp6_type
#define nd_ra_code nd_ra_hdr.icmp6_code
@@ -246,7 +245,7 @@ struct nd_neighbor_solicit { /* neighbor solicitation */
struct icmp6_hdr nd_ns_hdr;
struct in6_addr nd_ns_target; /*target address */
/* could be followed by options */
-} __packed;
+} __attribute__((__packed__));
#define nd_ns_type nd_ns_hdr.icmp6_type
#define nd_ns_code nd_ns_hdr.icmp6_code
@@ -257,7 +256,7 @@ struct nd_neighbor_advert { /* neighbor advertisement */
struct icmp6_hdr nd_na_hdr;
struct in6_addr nd_na_target; /* target address */
/* could be followed by options */
-} __packed;
+} __attribute__((__packed__));
#define nd_na_type nd_na_hdr.icmp6_type
#define nd_na_code nd_na_hdr.icmp6_code
@@ -280,7 +279,7 @@ struct nd_redirect { /* redirect */
struct in6_addr nd_rd_target; /* target address */
struct in6_addr nd_rd_dst; /* destination address */
/* could be followed by options */
-} __packed;
+} __attribute__((__packed__));
#define nd_rd_type nd_rd_hdr.icmp6_type
#define nd_rd_code nd_rd_hdr.icmp6_code
@@ -291,7 +290,7 @@ struct nd_opt_hdr { /* Neighbor discovery option header */
u_int8_t nd_opt_type;
u_int8_t nd_opt_len;
/* followed by option specific data*/
-} __packed;
+} __attribute__((__packed__));
#define ND_OPT_SOURCE_LINKADDR 1
#define ND_OPT_TARGET_LINKADDR 2
@@ -310,7 +309,7 @@ struct nd_opt_prefix_info { /* prefix information */
u_int32_t nd_opt_pi_preferred_time;
u_int32_t nd_opt_pi_reserved2;
struct in6_addr nd_opt_pi_prefix;
-} __packed;
+} __attribute__((__packed__));
#define ND_OPT_PI_FLAG_ONLINK 0x80
#define ND_OPT_PI_FLAG_AUTO 0x40
@@ -321,14 +320,14 @@ struct nd_opt_rd_hdr { /* redirected header */
u_int16_t nd_opt_rh_reserved1;
u_int32_t nd_opt_rh_reserved2;
/* followed by IP header and data */
-} __packed;
+} __attribute__((__packed__));
struct nd_opt_mtu { /* MTU option */
u_int8_t nd_opt_mtu_type;
u_int8_t nd_opt_mtu_len;
u_int16_t nd_opt_mtu_reserved;
u_int32_t nd_opt_mtu_mtu;
-} __packed;
+} __attribute__((__packed__));
struct nd_opt_route_info { /* route info */
u_int8_t nd_opt_rti_type;
@@ -337,7 +336,7 @@ struct nd_opt_route_info { /* route info */
u_int8_t nd_opt_rti_flags;
u_int32_t nd_opt_rti_lifetime;
/* prefix follows */
-} __packed;
+} __attribute__((__packed__));
/*
* icmp6 namelookup
@@ -352,7 +351,7 @@ struct icmp6_namelookup {
u_int8_t icmp6_nl_name[3];
#endif
/* could be followed by options */
-} __packed;
+} __attribute__((__packed__));
/*
* icmp6 node information
@@ -361,7 +360,7 @@ struct icmp6_nodeinfo {
struct icmp6_hdr icmp6_ni_hdr;
u_int8_t icmp6_ni_nonce[8];
/* could be followed by reply data */
-} __packed;
+} __attribute__((__packed__));
#define ni_type icmp6_ni_hdr.icmp6_type
#define ni_code icmp6_ni_hdr.icmp6_code
@@ -424,7 +423,7 @@ struct ni_reply_fqdn {
u_int32_t ni_fqdn_ttl; /* TTL */
u_int8_t ni_fqdn_namelen; /* length in octets of the FQDN */
u_int8_t ni_fqdn_name[3]; /* XXX: alignment */
-} __packed;
+} __attribute__((__packed__));
/*
* Router Renumbering. as router-renum-08.txt
@@ -435,7 +434,7 @@ struct icmp6_router_renum { /* router renumbering header */
u_int8_t rr_flags;
u_int16_t rr_maxdelay;
u_int32_t rr_reserved;
-} __packed;
+} __attribute__((__packed__));
#define ICMP6_RR_FLAGS_TEST 0x80
#define ICMP6_RR_FLAGS_REQRESULT 0x40
@@ -457,7 +456,7 @@ struct rr_pco_match { /* match prefix part */
u_int8_t rpm_maxlen;
u_int16_t rpm_reserved;
struct in6_addr rpm_prefix;
-} __packed;
+} __attribute__((__packed__));
#define RPM_PCO_ADD 1
#define RPM_PCO_CHANGE 2
@@ -473,7 +472,7 @@ struct rr_pco_use { /* use prefix part */
u_int32_t rpu_pltime;
u_int32_t rpu_flags;
struct in6_addr rpu_prefix;
-} __packed;
+} __attribute__((__packed__));
#define ICMP6_RR_PCOUSE_RAFLAGS_ONLINK 0x80
#define ICMP6_RR_PCOUSE_RAFLAGS_AUTO 0x40
@@ -491,7 +490,7 @@ struct rr_result { /* router renumbering result message */
u_int8_t rrr_matchedlen;
u_int32_t rrr_ifid;
struct in6_addr rrr_prefix;
-} __packed;
+} __attribute__((__packed__));
#if BYTE_ORDER == BIG_ENDIAN
#define ICMP6_RR_RESULT_FLAGS_OOB 0x0002
#define ICMP6_RR_RESULT_FLAGS_FORBIDDEN 0x0001
diff --git a/sys/netinet/in.h b/sys/netinet/in.h
index 8aafb24..039c105 100644
--- a/sys/netinet/in.h
+++ b/sys/netinet/in.h
@@ -39,6 +39,7 @@
#include <sys/cdefs.h>
#include <sys/_types.h>
+#include <sys/socket.h>
#include <machine/endian.h>
/* Protocols common to RFC 1700, POSIX, and X/Open. */
diff --git a/sys/netinet/in_pcb.h b/sys/netinet/in_pcb.h
index 96521e4..e8857bc 100644
--- a/sys/netinet/in_pcb.h
+++ b/sys/netinet/in_pcb.h
@@ -173,7 +173,6 @@ struct inpcb {
int inp6_cksum;
u_short inp6_ifindex;
short inp6_hops;
- u_int8_t inp6_hlim;
} inp_depend6;
LIST_ENTRY(inpcb) inp_portlist;
struct inpcbport *inp_phd; /* head of this list */
@@ -183,7 +182,6 @@ struct inpcb {
#define in6p_faddr inp_inc.inc6_faddr
#define in6p_laddr inp_inc.inc6_laddr
#define in6p_route inp_inc.inc6_route
-#define in6p_ip6_hlim inp_depend6.inp6_hlim
#define in6p_hops inp_depend6.inp6_hops /* default hop limit */
#define in6p_ip6_nxt inp_ip_p
#define in6p_flowinfo inp_flow
@@ -286,16 +284,20 @@ struct inpcbinfo { /* XXX documentation, prefixes */
#define IN6P_HOPOPTS 0x040000 /* receive hop-by-hop options */
#define IN6P_DSTOPTS 0x080000 /* receive dst options after rthdr */
#define IN6P_RTHDR 0x100000 /* receive routing header */
+#define IN6P_TCLASS 0x400000 /* receive traffic class value */
#define IN6P_RTHDRDSTOPTS 0x200000 /* receive dstoptions before rthdr */
#define IN6P_AUTOFLOWLABEL 0x800000 /* attach flowlabel automatically */
+#define IN6P_RFC2292 0x40000000 /* used RFC2292 API on the socket */
+#define IN6P_MTU 0x80000000 /* receive path MTU */
#define INP_CONTROLOPTS (INP_RECVOPTS|INP_RECVRETOPTS|INP_RECVDSTADDR|\
INP_RECVIF|INP_RECVTTL|\
IN6P_PKTINFO|IN6P_HOPLIMIT|IN6P_HOPOPTS|\
IN6P_DSTOPTS|IN6P_RTHDR|IN6P_RTHDRDSTOPTS|\
- IN6P_AUTOFLOWLABEL)
+ IN6P_TCLASS|IN6P_AUTOFLOWLABEL|IN6P_RFC2292|\
+ IN6P_MTU)
#define INP_UNMAPPABLEOPTS (IN6P_HOPOPTS|IN6P_DSTOPTS|IN6P_RTHDR|\
- IN6P_AUTOFLOWLABEL)
+ IN6P_TCLASS|IN6P_AUTOFLOWLABEL)
/* for KAME src sync over BSD*'s */
#define IN6P_HIGHPORT INP_HIGHPORT
diff --git a/sys/netinet/ip6.h b/sys/netinet/ip6.h
index e8dc156..ae66264 100644
--- a/sys/netinet/ip6.h
+++ b/sys/netinet/ip6.h
@@ -85,7 +85,7 @@ struct ip6_hdr {
} ip6_ctlun;
struct in6_addr ip6_src; /* source address */
struct in6_addr ip6_dst; /* destination address */
-} __packed;
+} __attribute__((__packed__));
#define ip6_vfc ip6_ctlun.ip6_un2_vfc
#define ip6_flow ip6_ctlun.ip6_un1.ip6_un1_flow
@@ -119,7 +119,7 @@ struct ip6_hdr {
struct ip6_ext {
u_int8_t ip6e_nxt;
u_int8_t ip6e_len;
-} __packed;
+} __attribute__((__packed__));
/* Hop-by-Hop options header */
/* XXX should we pad it to force alignment on an 8-byte boundary? */
@@ -127,7 +127,7 @@ struct ip6_hbh {
u_int8_t ip6h_nxt; /* next header */
u_int8_t ip6h_len; /* length in units of 8 octets */
/* followed by options */
-} __packed;
+} __attribute__((__packed__));
/* Destination options header */
/* XXX should we pad it to force alignment on an 8-byte boundary? */
@@ -135,7 +135,7 @@ struct ip6_dest {
u_int8_t ip6d_nxt; /* next header */
u_int8_t ip6d_len; /* length in units of 8 octets */
/* followed by options */
-} __packed;
+} __attribute__((__packed__));
/* Option types and related macros */
#define IP6OPT_PAD1 0x00 /* 00 0 00000 */
@@ -143,7 +143,10 @@ struct ip6_dest {
#define IP6OPT_JUMBO 0xC2 /* 11 0 00010 = 194 */
#define IP6OPT_NSAP_ADDR 0xC3 /* 11 0 00011 */
#define IP6OPT_TUNNEL_LIMIT 0x04 /* 00 0 00100 */
+#ifndef _KERNEL
#define IP6OPT_RTALERT 0x05 /* 00 0 00101 (KAME definition) */
+#endif
+#define IP6OPT_ROUTER_ALERT 0x05 /* 00 0 00101 (2292bis, recommended) */
#define IP6OPT_RTALERT_LEN 4
#define IP6OPT_RTALERT_MLD 0 /* Datagram contains an MLD message */
@@ -151,10 +154,6 @@ struct ip6_dest {
#define IP6OPT_RTALERT_ACTNET 2 /* contains an Active Networks msg */
#define IP6OPT_MINLEN 2
-#define IP6OPT_BINDING_UPDATE 0xc6 /* 11 0 00110 */
-#define IP6OPT_BINDING_ACK 0x07 /* 00 0 00111 */
-#define IP6OPT_BINDING_REQ 0x08 /* 00 0 01000 */
-#define IP6OPT_HOME_ADDRESS 0xc9 /* 11 0 01001 */
#define IP6OPT_EID 0x8a /* 10 0 01010 */
#define IP6OPT_TYPE(o) ((o) & 0xC0)
@@ -174,7 +173,7 @@ struct ip6_rthdr {
u_int8_t ip6r_type; /* routing type */
u_int8_t ip6r_segleft; /* segments left */
/* followed by routing type specific data */
-} __packed;
+} __attribute__((__packed__));
/* Type 0 Routing header */
struct ip6_rthdr0 {
@@ -185,7 +184,7 @@ struct ip6_rthdr0 {
u_int8_t ip6r0_reserved; /* reserved field */
u_int8_t ip6r0_slmap[3]; /* strict/loose bit map */
struct in6_addr ip6r0_addr[1]; /* up to 23 addresses */
-} __packed;
+} __attribute__((__packed__));
/* Fragment header */
struct ip6_frag {
@@ -193,7 +192,7 @@ struct ip6_frag {
u_int8_t ip6f_reserved; /* reserved field */
u_int16_t ip6f_offlg; /* offset, reserved, and flag */
u_int32_t ip6f_ident; /* identification */
-} __packed;
+} __attribute__((__packed__));
#if BYTE_ORDER == BIG_ENDIAN
#define IP6F_OFF_MASK 0xfff8 /* mask out offset from _offlg */
diff --git a/sys/netinet6/icmp6.c b/sys/netinet6/icmp6.c
index aa96326..30e534f 100644
--- a/sys/netinet6/icmp6.c
+++ b/sys/netinet6/icmp6.c
@@ -245,9 +245,15 @@ icmp6_error(m, type, code, param)
oip6 = mtod(m, struct ip6_hdr *);
/*
- * Multicast destination check. For unrecognized option errors,
- * this check has already done in ip6_unknown_opt(), so we can
- * check only for other errors.
+ * If the destination address of the erroneous packet is a multicast
+ * address, or the packet was sent using link-layer multicast,
+ * we should basically suppress sending an error (RFC 2463, Section
+ * 2.4).
+ * We have two exceptions (the item e.2 in that section):
+ * - the Pakcet Too Big message can be sent for path MTU discovery.
+ * - the Parameter Problem Message that can be allowed an icmp6 error
+ * in the option type field. This check has been done in
+ * ip6_unknown_opt(), so we can just check the type and code.
*/
if ((m->m_flags & (M_BCAST|M_MCAST) ||
IN6_IS_ADDR_MULTICAST(&oip6->ip6_dst)) &&
@@ -256,7 +262,10 @@ icmp6_error(m, type, code, param)
code != ICMP6_PARAMPROB_OPTION)))
goto freeit;
- /* Source address check. XXX: the case of anycast source? */
+ /*
+ * RFC 2463, 2.4 (e.5): source address check.
+ * XXX: the case of anycast source?
+ */
if (IN6_IS_ADDR_UNSPECIFIED(&oip6->ip6_src) ||
IN6_IS_ADDR_MULTICAST(&oip6->ip6_src))
goto freeit;
@@ -1100,6 +1109,26 @@ icmp6_mtudisc_update(ip6cp, validated)
struct rtentry *rt = NULL;
struct sockaddr_in6 sin6;
+#if 0
+ /*
+ * RFC2460 section 5, last paragraph.
+ * even though minimum link MTU for IPv6 is IPV6_MMTU,
+ * we may see ICMPv6 too big with mtu < IPV6_MMTU
+ * due to packet translator in the middle.
+ * see ip6_output() and ip6_getpmtu() "alwaysfrag" case for
+ * special handling.
+ */
+ if (mtu < IPV6_MMTU)
+ return;
+#endif
+
+ /*
+ * we reject ICMPv6 too big with abnormally small value.
+ * XXX what is the good definition of "abnormally small"?
+ */
+ if (mtu < sizeof(struct ip6_hdr) + sizeof(struct ip6_frag) + 8)
+ return;
+
if (!validated)
return;
@@ -2122,7 +2151,9 @@ icmp6_reflect(m, off)
ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
ip6->ip6_vfc |= IPV6_VERSION;
ip6->ip6_nxt = IPPROTO_ICMPV6;
- if (m->m_pkthdr.rcvif) {
+ if (outif)
+ ip6->ip6_hlim = ND_IFINFO(outif)->chlim;
+ else if (m->m_pkthdr.rcvif) {
/* XXX: This may not be the outgoing interface */
ip6->ip6_hlim = ND_IFINFO(m->m_pkthdr.rcvif)->chlim;
} else
diff --git a/sys/netinet6/in6.h b/sys/netinet6/in6.h
index 92127f3..45481e9 100644
--- a/sys/netinet6/in6.h
+++ b/sys/netinet6/in6.h
@@ -407,15 +407,6 @@ struct route_in6 {
* Options for use with [gs]etsockopt at the IPV6 level.
* First word of comment is data type; bool is stored in int.
*/
-#define IPV6_JOIN_GROUP 12 /* ip6_mreq; join a group membership */
-#define IPV6_LEAVE_GROUP 13 /* ip6_mreq; leave a group membership */
-#define IPV6_MULTICAST_HOPS 10 /* int; set/get IP6 multicast hops */
-#define IPV6_MULTICAST_IF 9 /* u_int; set/get IP6 multicast i/f */
-#define IPV6_MULTICAST_LOOP 11 /* u_int; set/get IP6 multicast loopback */
-#define IPV6_UNICAST_HOPS 4 /* int; IP6 hops */
-#define IPV6_V6ONLY 27 /* bool; only bind INET6 at wildcard bind */
-
-#if __BSD_VISIBLE
/* no hdrincl */
#if 0 /* the followings are relic in IPv4 and hence are disabled */
#define IPV6_OPTIONS 1 /* buf/ip6_opts; set/get IP6 options */
@@ -425,18 +416,27 @@ struct route_in6 {
#define IPV6_RETOPTS 8 /* ip6_opts; set/get IP6 options */
#endif
#define IPV6_SOCKOPT_RESERVED1 3 /* reserved for future use */
+#define IPV6_UNICAST_HOPS 4 /* int; IP6 hops */
+#define IPV6_MULTICAST_IF 9 /* u_int; set/get IP6 multicast i/f */
+#define IPV6_MULTICAST_HOPS 10 /* int; set/get IP6 multicast hops */
+#define IPV6_MULTICAST_LOOP 11 /* u_int; set/get IP6 multicast loopback */
+#define IPV6_JOIN_GROUP 12 /* ip6_mreq; join a group membership */
+#define IPV6_LEAVE_GROUP 13 /* ip6_mreq; leave a group membership */
#define IPV6_PORTRANGE 14 /* int; range to choose for unspec port */
#define ICMP6_FILTER 18 /* icmp6_filter; icmp6 filter */
/* RFC2292 options */
-#define IPV6_PKTINFO 19 /* bool; send/recv if, src/dst addr */
-#define IPV6_HOPLIMIT 20 /* bool; hop limit */
-#define IPV6_NEXTHOP 21 /* bool; next hop addr */
-#define IPV6_HOPOPTS 22 /* bool; hop-by-hop option */
-#define IPV6_DSTOPTS 23 /* bool; destination option */
-#define IPV6_RTHDR 24 /* bool; routing header */
-#define IPV6_PKTOPTIONS 25 /* buf/cmsghdr; set/get IPv6 options */
+#ifdef _KERNEL
+#define IPV6_2292PKTINFO 19 /* bool; send/recv if, src/dst addr */
+#define IPV6_2292HOPLIMIT 20 /* bool; hop limit */
+#define IPV6_2292NEXTHOP 21 /* bool; next hop addr */
+#define IPV6_2292HOPOPTS 22 /* bool; hop-by-hop option */
+#define IPV6_2292DSTOPTS 23 /* bool; destinaion option */
+#define IPV6_2292RTHDR 24 /* bool; routing header */
+#define IPV6_2292PKTOPTIONS 25 /* buf/cmsghdr; set/get IPv6 options */
+#endif
#define IPV6_CHECKSUM 26 /* int; checksum offset for raw socket */
+#define IPV6_V6ONLY 27 /* bool; make AF_INET6 sockets v6 only */
#ifndef _KERNEL
#define IPV6_BINDV6ONLY IPV6_V6ONLY
#endif
@@ -454,6 +454,51 @@ struct route_in6 {
#define IPV6_FW_GET 34 /* get entire firewall rule chain */
#endif
+/* new socket options introduced in RFC2292bis */
+#define IPV6_RTHDRDSTOPTS 35 /* ip6_dest; send dst option before rthdr */
+
+#define IPV6_RECVPKTINFO 36 /* bool; recv if, dst addr */
+#define IPV6_RECVHOPLIMIT 37 /* bool; recv hop limit */
+#define IPV6_RECVRTHDR 38 /* bool; recv routing header */
+#define IPV6_RECVHOPOPTS 39 /* bool; recv hop-by-hop option */
+#define IPV6_RECVDSTOPTS 40 /* bool; recv dst option after rthdr */
+#ifdef _KERNEL
+#define IPV6_RECVRTHDRDSTOPTS 41 /* bool; recv dst option before rthdr */
+#endif
+
+#define IPV6_USE_MIN_MTU 42 /* bool; send packets at the minimum MTU */
+#define IPV6_RECVPATHMTU 43 /* bool; notify an according MTU */
+
+#define IPV6_PATHMTU 44 /* mtuinfo; get the current path MTU (sopt),
+ 4 bytes int; MTU notification (cmsg) */
+#if 0 /*obsoleted during 2292bis -> 3542*/
+#define IPV6_REACHCONF 45 /* no data; ND reachability confirm
+ (cmsg only/not in of RFC3542) */
+#endif
+
+/* more new socket options introduced in RFC2292bis */
+#define IPV6_PKTINFO 46 /* in6_pktinfo; send if, src addr */
+#define IPV6_HOPLIMIT 47 /* int; send hop limit */
+#define IPV6_NEXTHOP 48 /* sockaddr; next hop addr */
+#define IPV6_HOPOPTS 49 /* ip6_hbh; send hop-by-hop option */
+#define IPV6_DSTOPTS 50 /* ip6_dest; send dst option befor rthdr */
+#define IPV6_RTHDR 51 /* ip6_rthdr; send routing header */
+#if 0
+#define IPV6_PKTOPTIONS 52 /* buf/cmsghdr; set/get IPv6 options */
+ /* obsoleted by 2292bis */
+#endif
+
+#define IPV6_RECVTCLASS 57 /* bool; recv traffic class values */
+
+#define IPV6_AUTOFLOWLABEL 59 /* bool; attach flowlabel automagically */
+
+#define IPV6_TCLASS 61 /* int; send traffic class value */
+#define IPV6_DONTFRAG 62 /* bool; disable IPv6 fragmentation */
+
+#define IPV6_PREFER_TEMPADDR 63 /* int; prefer temporary addresses as
+ * the source address.
+ */
+
/* to define items, should talk with KAME guys first, for *BSD compatibility */
#define IPV6_RTHDR_LOOSE 0 /* this hop need not be a neighbor. XXX old spec */
@@ -483,6 +528,14 @@ struct in6_pktinfo {
};
/*
+ * Control structure for IPV6_RECVPATHMTU socket option.
+ */
+struct ip6_mtuinfo {
+ struct sockaddr_in6 ip6m_addr; /* or sockaddr_storage? */
+ u_int32_t ip6m_mtu;
+};
+
+/*
* Argument for IPV6_PORTRANGE:
* - which range to search when port is unspecified at bind() or connect()
*/
@@ -490,6 +543,7 @@ struct in6_pktinfo {
#define IPV6_PORTRANGE_HIGH 1 /* "high" - request firewall bypass */
#define IPV6_PORTRANGE_LOW 2 /* "low" - vouchsafe security */
+#if __BSD_VISIBLE
/*
* Definitions for inet6 sysctl operations.
*
@@ -544,6 +598,7 @@ struct in6_pktinfo {
/* New entries should be added here from current IPV6CTL_MAXID value. */
/* to define items, should talk with KAME guys first, for *BSD compatibility */
#define IPV6CTL_MAXID 42
+#endif /* __BSD_VISIBLE */
/*
* Redefinition of mbuf flags
@@ -584,6 +639,8 @@ typedef __size_t size_t;
#define _SIZE_T_DECLARED
#endif
+#if __BSD_VISIBLE
+
__BEGIN_DECLS
struct cmsghdr;
@@ -591,14 +648,14 @@ extern int inet6_option_space __P((int));
extern int inet6_option_init __P((void *, struct cmsghdr **, int));
extern int inet6_option_append __P((struct cmsghdr *, const uint8_t *,
int, int));
-extern uint8_t *inet6_option_alloc __P((struct cmsghdr *, int, int, int));
-extern int inet6_option_next __P((const struct cmsghdr *, uint8_t **));
-extern int inet6_option_find __P((const struct cmsghdr *, uint8_t **, int));
+extern u_int8_t *inet6_option_alloc __P((struct cmsghdr *, int, int, int));
+extern int inet6_option_next __P((const struct cmsghdr *, u_int8_t **));
+extern int inet6_option_find __P((const struct cmsghdr *, u_int8_t **, int));
extern size_t inet6_rthdr_space __P((int, int));
extern struct cmsghdr *inet6_rthdr_init __P((void *, int));
extern int inet6_rthdr_add __P((struct cmsghdr *, const struct in6_addr *,
- unsigned int));
+ unsigned int));
extern int inet6_rthdr_lasthop __P((struct cmsghdr *, unsigned int));
#if 0 /* not implemented yet */
extern int inet6_rthdr_reverse __P((const struct cmsghdr *, struct cmsghdr *));
@@ -607,19 +664,19 @@ extern int inet6_rthdr_segments __P((const struct cmsghdr *));
extern struct in6_addr *inet6_rthdr_getaddr __P((struct cmsghdr *, int));
extern int inet6_rthdr_getflags __P((const struct cmsghdr *, int));
-extern int inet6_opt_init __P((void *, size_t));
-extern int inet6_opt_append __P((void *, size_t, int, uint8_t,
- size_t, uint8_t, void **));
-extern int inet6_opt_finish __P((void *, size_t, int));
-extern int inet6_opt_set_val __P((void *, size_t, void *, int));
-
-extern int inet6_opt_next __P((void *, size_t, int, uint8_t *,
- size_t *, void **));
-extern int inet6_opt_find __P((void *, size_t, int, uint8_t,
- size_t *, void **));
-extern int inet6_opt_get_val __P((void *, size_t, void *, int));
-extern size_t inet6_rth_space __P((int, int));
-extern void *inet6_rth_init __P((void *, int, int, int));
+extern int inet6_opt_init __P((void *, socklen_t));
+extern int inet6_opt_append __P((void *, socklen_t, int, u_int8_t, socklen_t,
+ u_int8_t, void **));
+extern int inet6_opt_finish __P((void *, socklen_t, int));
+extern int inet6_opt_set_val __P((void *, int, void *, socklen_t));
+
+extern int inet6_opt_next __P((void *, socklen_t, int, u_int8_t *, socklen_t *,
+ void **));
+extern int inet6_opt_find __P((void *, socklen_t, int, u_int8_t, socklen_t *,
+ void **));
+extern int inet6_opt_get_val __P((void *, int, void *, socklen_t));
+extern socklen_t inet6_rth_space __P((int, int));
+extern void *inet6_rth_init __P((void *, socklen_t, int, int));
extern int inet6_rth_add __P((void *, const struct in6_addr *));
extern int inet6_rth_reverse __P((const void *, void *));
extern int inet6_rth_segments __P((const void *));
diff --git a/sys/netinet6/in6_pcb.c b/sys/netinet6/in6_pcb.c
index a49bb59..eb62328 100644
--- a/sys/netinet6/in6_pcb.c
+++ b/sys/netinet6/in6_pcb.c
@@ -444,8 +444,7 @@ in6_pcbdetach(inp)
so->so_pcb = NULL;
sotryfree(so);
}
- if (inp->in6p_options)
- m_freem(inp->in6p_options);
+
ip6_freepcbopts(inp->in6p_outputopts);
ip6_freemoptions(inp->in6p_moptions);
if (inp->in6p_route.ro_rt)
diff --git a/sys/netinet6/in6_var.h b/sys/netinet6/in6_var.h
index 5a520fa..b481b04 100644
--- a/sys/netinet6/in6_var.h
+++ b/sys/netinet6/in6_var.h
@@ -391,7 +391,10 @@ struct in6_rrenumreq {
#define SIOCGIFAFLAG_IN6 _IOWR('i', 73, struct in6_ifreq)
#define SIOCGDRLST_IN6 _IOWR('i', 74, struct in6_drlist)
-#define SIOCGPRLST_IN6 _IOWR('i', 75, struct in6_prlist)
+#ifdef _KERNEL
+/* XXX: SIOCGPRLST_IN6 is exposed in KAME but in6_oprlist is not. */
+#define SIOCGPRLST_IN6 _IOWR('i', 75, struct in6_oprlist)
+#endif
#ifdef _KERNEL
#define OSIOCGIFINFO_IN6 _IOWR('i', 76, struct in6_ondireq)
#endif
diff --git a/sys/netinet6/ip6_input.c b/sys/netinet6/ip6_input.c
index 54f252d..b823097 100644
--- a/sys/netinet6/ip6_input.c
+++ b/sys/netinet6/ip6_input.c
@@ -916,7 +916,7 @@ ip6_process_hopopts(m, opthead, hbhlen, rtalertp, plenp)
}
optlen = *(opt + 1) + 2;
break;
- case IP6OPT_RTALERT:
+ case IP6OPT_ROUTER_ALERT:
/* XXX may need check for alignment */
if (hbhlen < IP6OPT_RTALERT_LEN) {
ip6stat.ip6s_toosmall++;
@@ -1077,14 +1077,9 @@ ip6_savecontrol(in6p, mp, ip6, m)
struct ip6_hdr *ip6;
struct mbuf *m;
{
-#if __FreeBSD_version >= 500000
+#define IS2292(x, y) ((in6p->in6p_flags & IN6P_RFC2292) ? (x) : (y))
struct thread *td = curthread; /* XXX */
-#else
- struct proc *td = curproc; /* XXX */
-#endif
int privileged = 0;
- int rthdr_exist = 0;
-
if (td && !suser(td))
privileged++;
@@ -1096,9 +1091,8 @@ ip6_savecontrol(in6p, mp, ip6, m)
microtime(&tv);
*mp = sbcreatecontrol((caddr_t) &tv, sizeof(tv),
SCM_TIMESTAMP, SOL_SOCKET);
- if (*mp) {
+ if (*mp)
mp = &(*mp)->m_next;
- }
}
#endif
@@ -1113,20 +1107,32 @@ ip6_savecontrol(in6p, mp, ip6, m)
*mp = sbcreatecontrol((caddr_t) &pi6,
sizeof(struct in6_pktinfo),
- IPV6_PKTINFO, IPPROTO_IPV6);
- if (*mp) {
+ IS2292(IPV6_2292PKTINFO, IPV6_PKTINFO), IPPROTO_IPV6);
+ if (*mp)
mp = &(*mp)->m_next;
- }
}
if ((in6p->in6p_flags & IN6P_HOPLIMIT) != 0) {
int hlim = ip6->ip6_hlim & 0xff;
*mp = sbcreatecontrol((caddr_t) &hlim, sizeof(int),
- IPV6_HOPLIMIT, IPPROTO_IPV6);
- if (*mp) {
+ IS2292(IPV6_2292HOPLIMIT, IPV6_HOPLIMIT), IPPROTO_IPV6);
+ if (*mp)
+ mp = &(*mp)->m_next;
+ }
+
+ if ((in6p->in6p_flags & IN6P_TCLASS) != 0) {
+ u_int32_t flowinfo;
+ int tclass;
+
+ flowinfo = (u_int32_t)ntohl(ip6->ip6_flow & IPV6_FLOWINFO_MASK);
+ flowinfo >>= 20;
+
+ tclass = flowinfo & 0xff;
+ *mp = sbcreatecontrol((caddr_t) &tclass, sizeof(tclass),
+ IPV6_TCLASS, IPPROTO_IPV6);
+ if (*mp)
mp = &(*mp)->m_next;
- }
}
/*
@@ -1135,7 +1141,11 @@ ip6_savecontrol(in6p, mp, ip6, m)
* be some hop-by-hop options which can be returned to normal user.
* See RFC 2292 section 6.
*/
- if ((in6p->in6p_flags & IN6P_HOPOPTS) != 0 && privileged) {
+ if ((in6p->in6p_flags & IN6P_HOPOPTS) != 0) {
+#ifdef DIAGNOSTIC
+ if (!privileged)
+ panic("IN6P_HOPOPTS is set for unprivileged socket");
+#endif
/*
* Check if a hop-by-hop options header is contatined in the
* received packet, and if so, store the options as ancillary
@@ -1143,7 +1153,6 @@ ip6_savecontrol(in6p, mp, ip6, m)
* just after the IPv6 header, which is assured through the
* IPv6 input processing.
*/
- struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
if (ip6->ip6_nxt == IPPROTO_HOPOPTS) {
struct ip6_hbh *hbh;
int hbhlen = 0;
@@ -1178,50 +1187,17 @@ ip6_savecontrol(in6p, mp, ip6, m)
* Note: this constraint is removed in 2292bis.
*/
*mp = sbcreatecontrol((caddr_t)hbh, hbhlen,
- IPV6_HOPOPTS, IPPROTO_IPV6);
- if (*mp) {
+ IS2292(IPV6_2292HOPOPTS, IPV6_HOPOPTS),
+ IPPROTO_IPV6);
+ if (*mp)
mp = &(*mp)->m_next;
- }
#ifdef PULLDOWN_TEST
m_freem(ext);
#endif
}
}
- /* IPV6_DSTOPTS and IPV6_RTHDR socket options */
- if ((in6p->in6p_flags & (IN6P_DSTOPTS | IN6P_RTHDRDSTOPTS)) != 0) {
- int proto, off, nxt;
-
- /*
- * go through the header chain to see if a routing header is
- * contained in the packet. We need this information to store
- * destination options headers (if any) properly.
- * XXX: performance issue. We should record this info when
- * processing extension headers in incoming routine.
- * (todo) use m_aux?
- */
- proto = IPPROTO_IPV6;
- off = 0;
- nxt = -1;
- while (1) {
- int newoff;
-
- newoff = ip6_nexthdr(m, off, proto, &nxt);
- if (newoff < 0)
- break;
- if (newoff < off) /* invalid, check for safety */
- break;
- if ((proto = nxt) == IPPROTO_ROUTING) {
- rthdr_exist = 1;
- break;
- }
- off = newoff;
- }
- }
-
- if ((in6p->in6p_flags &
- (IN6P_RTHDR | IN6P_DSTOPTS | IN6P_RTHDRDSTOPTS)) != 0) {
- struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
+ if ((in6p->in6p_flags & (IN6P_RTHDR | IN6P_DSTOPTS)) != 0) {
int nxt = ip6->ip6_nxt, off = sizeof(struct ip6_hdr);
/*
@@ -1293,7 +1269,7 @@ ip6_savecontrol(in6p, mp, ip6, m)
break;
*mp = sbcreatecontrol((caddr_t)ip6e, elen,
- IPV6_DSTOPTS,
+ IS2292(IPV6_2292DSTOPTS, IPV6_DSTOPTS),
IPPROTO_IPV6);
if (*mp)
mp = &(*mp)->m_next;
@@ -1303,7 +1279,7 @@ ip6_savecontrol(in6p, mp, ip6, m)
break;
*mp = sbcreatecontrol((caddr_t)ip6e, elen,
- IPV6_RTHDR,
+ IS2292(IPV6_2292RTHDR, IPV6_RTHDR),
IPPROTO_IPV6);
if (*mp)
mp = &(*mp)->m_next;
@@ -1339,6 +1315,7 @@ ip6_savecontrol(in6p, mp, ip6, m)
;
}
+#undef IS2292
}
#ifdef PULLDOWN_TEST
diff --git a/sys/netinet6/ip6_output.c b/sys/netinet6/ip6_output.c
index 51f5b7a..b4859ec 100644
--- a/sys/netinet6/ip6_output.c
+++ b/sys/netinet6/ip6_output.c
@@ -128,8 +128,14 @@ struct ip6_exthdrs {
struct mbuf *ip6e_dest2;
};
+static int ip6_pcbopt __P((int, u_char *, int, struct ip6_pktopts **,
+ int, int));
static int ip6_pcbopts __P((struct ip6_pktopts **, struct mbuf *,
struct socket *, struct sockopt *));
+static int ip6_getpcbopt __P((struct ip6_pktopts *, int, struct sockopt *));
+static int ip6_setpktoption __P((int, u_char *, int, struct ip6_pktopts *, int,
+ int, int, int));
+
static int ip6_setmoptions __P((int, struct ip6_moptions **, struct mbuf *));
static int ip6_getmoptions __P((int, struct ip6_moptions *, struct mbuf **));
static int ip6_copyexthdr __P((struct mbuf **, caddr_t, int));
@@ -138,7 +144,7 @@ static int ip6_insertfraghdr __P((struct mbuf *, struct mbuf *, int,
static int ip6_insert_jumboopt __P((struct ip6_exthdrs *, u_int32_t));
static int ip6_splithdr __P((struct mbuf *, struct ip6_exthdrs *));
static int ip6_getpmtu __P((struct route_in6 *, struct route_in6 *,
- struct ifnet *, struct in6_addr *, u_long *));
+ struct ifnet *, struct in6_addr *, u_long *, int *));
/*
@@ -171,6 +177,7 @@ ip6_output(m0, opt, ro, flags, im6o, ifpp, inp)
int error = 0;
struct in6_ifaddr *ia = NULL;
u_long mtu;
+ int alwaysfrag, dontfrag;
u_int32_t optlen = 0, plen = 0, unfragpartlen = 0;
struct ip6_exthdrs exthdrs;
struct in6_addr finaldst;
@@ -533,6 +540,33 @@ skip_ipsec2:;
dst->sin6_len = sizeof(struct sockaddr_in6);
dst->sin6_addr = ip6->ip6_dst;
}
+
+ /*
+ * if specified, try to fill in the traffic class field.
+ * do not override if a non-zero value is already set.
+ * we check the diffserv field and the ecn field separately.
+ */
+ if (opt && opt->ip6po_tclass >= 0) {
+ int mask = 0;
+
+ if ((ip6->ip6_flow & htonl(0xfc << 20)) == 0)
+ mask |= 0xfc;
+ if ((ip6->ip6_flow & htonl(0x03 << 20)) == 0)
+ mask |= 0x03;
+ if (mask != 0)
+ ip6->ip6_flow |= htonl((opt->ip6po_tclass & mask) << 20);
+ }
+
+ /* fill in or override the hop limit field, if necessary. */
+ if (opt && opt->ip6po_hlim != -1)
+ ip6->ip6_hlim = opt->ip6po_hlim & 0xff;
+ else if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
+ if (im6o != NULL)
+ ip6->ip6_hlim = im6o->im6o_multicast_hlim;
+ else
+ ip6->ip6_hlim = ip6_defmcasthlim;
+ }
+
#if defined(IPSEC) || defined(FAST_IPSEC)
if (needipsec && needipsectun) {
struct ipsec_output_state state;
@@ -760,7 +794,8 @@ skip_ipsec2:;
* loop back a copy if this host actually belongs to the
* destination group on the loopback interface.
*/
- if (ip6->ip6_hlim == 0 || (ifp->if_flags & IFF_LOOPBACK)) {
+ if (ip6->ip6_hlim == 0 || (ifp->if_flags & IFF_LOOPBACK) ||
+ IN6_IS_ADDR_MC_INTFACELOCAL(&ip6->ip6_dst)) {
m_freem(m);
goto done;
}
@@ -774,7 +809,8 @@ skip_ipsec2:;
*ifpp = ifp;
/* Determine path MTU. */
- if ((error = ip6_getpmtu(ro_pmtu, ro, ifp, &finaldst, &mtu)) != 0)
+ if ((error = ip6_getpmtu(ro_pmtu, ro, ifp, &finaldst, &mtu,
+ &alwaysfrag)) != 0)
goto bad;
/*
@@ -891,40 +927,81 @@ skip_ipsec2:;
/*
* Send the packet to the outgoing interface.
* If necessary, do IPv6 fragmentation before sending.
+ *
+ * the logic here is rather complex:
+ * 1: normal case (dontfrag == 0, alwaysfrag == 0)
+ * 1-a: send as is if tlen <= path mtu
+ * 1-b: fragment if tlen > path mtu
+ *
+ * 2: if user asks us not to fragment (dontfrag == 1)
+ * 2-a: send as is if tlen <= interface mtu
+ * 2-b: error if tlen > interface mtu
+ *
+ * 3: if we always need to attach fragment header (alwaysfrag == 1)
+ * always fragment
+ *
+ * 4: if dontfrag == 1 && alwaysfrag == 1
+ * error, as we cannot handle this conflicting request
*/
tlen = m->m_pkthdr.len;
- if (tlen <= mtu
-#ifdef notyet
- /*
- * On any link that cannot convey a 1280-octet packet in one piece,
- * link-specific fragmentation and reassembly must be provided at
- * a layer below IPv6. [RFC 2460, sec.5]
- * Thus if the interface has ability of link-level fragmentation,
- * we can just send the packet even if the packet size is
- * larger than the link's MTU.
- * XXX: IFF_FRAGMENTABLE (or such) flag has not been defined yet...
- */
-
- || ifp->if_flags & IFF_FRAGMENTABLE
-#endif
- )
- {
- /* Record statistics for this interface address. */
- if (ia && !(flags & IPV6_FORWARDING)) {
- ia->ia_ifa.if_opackets++;
- ia->ia_ifa.if_obytes += m->m_pkthdr.len;
- }
+
+ if (opt && (opt->ip6po_flags & IP6PO_DONTFRAG))
+ dontfrag = 1;
+ else
+ dontfrag = 0;
+ if (dontfrag && alwaysfrag) { /* case 4 */
+ /* conflicting request - can't transmit */
+ error = EMSGSIZE;
+ goto bad;
+ }
+ if (dontfrag && tlen > IN6_LINKMTU(ifp)) { /* case 2-b */
+ /*
+ * Even if the DONTFRAG option is specified, we cannot send the
+ * packet when the data length is larger than the MTU of the
+ * outgoing interface.
+ * Notify the error by sending IPV6_PATHMTU ancillary data as
+ * well as returning an error code (the latter is not described
+ * in the API spec.)
+ */
+ u_int32_t mtu32;
+ struct ip6ctlparam ip6cp;
+
+ mtu32 = (u_int32_t)mtu;
+ bzero(&ip6cp, sizeof(ip6cp));
+ ip6cp.ip6c_cmdarg = (void *)&mtu32;
+ pfctlinput2(PRC_MSGSIZE, (struct sockaddr *)&ro_pmtu->ro_dst,
+ (void *)&ip6cp);
+
+ error = EMSGSIZE;
+ goto bad;
+ }
+
+ /*
+ * transmit packet without fragmentation
+ */
+ if (dontfrag || (!alwaysfrag && tlen <= mtu)) { /* case 1-a and 2-a */
+ struct in6_ifaddr *ia6;
+
+ ip6 = mtod(m, struct ip6_hdr *);
+ ia6 = in6_ifawithifp(ifp, &ip6->ip6_src);
+ if (ia6) {
+ /* Record statistics for this interface address. */
+ ia6->ia_ifa.if_opackets++;
+ ia6->ia_ifa.if_obytes += m->m_pkthdr.len;
+ }
#ifdef IPSEC
/* clean ipsec history once it goes out of the node */
ipsec_delaux(m);
#endif
error = nd6_output(ifp, origifp, m, dst, ro->ro_rt);
goto done;
- } else if (mtu < IPV6_MMTU) {
- /*
- * note that path MTU is never less than IPV6_MMTU
- * (see icmp6_input).
- */
+ }
+
+ /*
+ * try to fragment the packet. case 1-b and 3
+ */
+ if (mtu < IPV6_MMTU) {
+ /* path MTU cannot be less than IPV6_MMTU */
error = EMSGSIZE;
in6_ifstat_inc(ifp, ifs6_out_fragfail);
goto bad;
@@ -1261,22 +1338,21 @@ ip6_insertfraghdr(m0, m, hlen, frghdrp)
}
static int
-ip6_getpmtu(ro_pmtu, ro, ifp, dst, mtup)
+ip6_getpmtu(ro_pmtu, ro, ifp, dst, mtup, alwaysfragp)
struct route_in6 *ro_pmtu, *ro;
struct ifnet *ifp;
struct in6_addr *dst;
u_long *mtup;
+ int *alwaysfragp;
{
u_int32_t mtu = 0;
+ int alwaysfrag = 0;
int error = 0;
- /*
- * Determine path MTU.
- */
if (ro_pmtu != ro) {
/* The first hop and the final destination may differ. */
struct sockaddr_in6 *sa6_dst =
- (struct sockaddr_in6 *)&ro_pmtu->ro_dst;
+ (struct sockaddr_in6 *)&ro_pmtu->ro_dst;
if (ro_pmtu->ro_rt &&
((ro->ro_rt->rt_flags & RTF_UP) == 0 ||
!IN6_ARE_ADDR_EQUAL(&sa6_dst->sin6_addr, dst))) {
@@ -1301,7 +1377,18 @@ ip6_getpmtu(ro_pmtu, ro, ifp, dst, mtup)
mtu = ro_pmtu->ro_rt->rt_rmx.rmx_mtu;
if (mtu == 0)
mtu = ifmtu;
- else if (mtu > ifmtu || mtu == 0) {
+ else if (mtu < IPV6_MMTU) {
+ /*
+ * RFC2460 section 5, last paragraph:
+ * if we record ICMPv6 too big message with
+ * mtu < IPV6_MMTU, transmit packets sized IPV6_MMTU
+ * or smaller, with framgent header attached.
+ * (fragment header is needed regardless from the
+ * packet size, for translators to identify packets)
+ */
+ alwaysfrag = 1;
+ mtu = IPV6_MMTU;
+ } else if (mtu > ifmtu) {
/*
* The MTU on the route is larger than the MTU on
* the interface! This shouldn't happen, unless the
@@ -1320,6 +1407,8 @@ ip6_getpmtu(ro_pmtu, ro, ifp, dst, mtup)
error = EHOSTUNREACH; /* XXX */
*mtup = mtu;
+ if (alwaysfragp)
+ *alwaysfragp = alwaysfrag;
return (error);
}
@@ -1331,7 +1420,8 @@ ip6_ctloutput(so, sopt)
struct socket *so;
struct sockopt *sopt;
{
- int privileged;
+ int privileged, optdatalen, uproto;
+ void *optdata;
struct inpcb *in6p = sotoinpcb(so);
int error, optval;
int level, op, optname;
@@ -1350,13 +1440,17 @@ ip6_ctloutput(so, sopt)
error = optval = 0;
privileged = (td == 0 || suser(td)) ? 0 : 1;
+ uproto = (int)so->so_proto->pr_protocol;
if (level == IPPROTO_IPV6) {
switch (op) {
case SOPT_SET:
switch (optname) {
+ case IPV6_2292PKTOPTIONS:
+#ifdef IPV6_PKTOPTIONS
case IPV6_PKTOPTIONS:
+#endif
{
struct mbuf *m;
@@ -1385,11 +1479,25 @@ ip6_ctloutput(so, sopt)
* receiving ANY hbh/dst options in order to avoid
* overhead of parsing options in the kernel.
*/
+ case IPV6_RECVHOPOPTS:
+ case IPV6_RECVDSTOPTS:
+ case IPV6_RECVRTHDRDSTOPTS:
+ if (!privileged) {
+ error = EPERM;
+ break;
+ }
+ /* FALLTHROUGH */
case IPV6_UNICAST_HOPS:
- case IPV6_CHECKSUM:
+ case IPV6_HOPLIMIT:
case IPV6_FAITH:
+ case IPV6_RECVPKTINFO:
+ case IPV6_RECVHOPLIMIT:
+ case IPV6_RECVRTHDR:
+ case IPV6_RECVPATHMTU:
+ case IPV6_RECVTCLASS:
case IPV6_V6ONLY:
+ case IPV6_AUTOFLOWLABEL:
if (optlen != sizeof(int)) {
error = EINVAL;
break;
@@ -1418,16 +1526,103 @@ do { \
else \
in6p->in6p_flags &= ~(bit); \
} while (/*CONSTCOND*/ 0)
+#define OPTSET2292(bit) \
+do { \
+ in6p->in6p_flags |= IN6P_RFC2292; \
+ if (optval) \
+ in6p->in6p_flags |= (bit); \
+ else \
+ in6p->in6p_flags &= ~(bit); \
+} while (/*CONSTCOND*/ 0)
#define OPTBIT(bit) (in6p->in6p_flags & (bit) ? 1 : 0)
- case IPV6_CHECKSUM:
- in6p->in6p_cksum = optval;
+ case IPV6_RECVPKTINFO:
+ /* cannot mix with RFC2292 */
+ if (OPTBIT(IN6P_RFC2292)) {
+ error = EINVAL;
+ break;
+ }
+ OPTSET(IN6P_PKTINFO);
+ break;
+
+ case IPV6_HOPLIMIT:
+ {
+ struct ip6_pktopts **optp;
+
+ /* cannot mix with RFC2292 */
+ if (OPTBIT(IN6P_RFC2292)) {
+ error = EINVAL;
+ break;
+ }
+ optp = &in6p->in6p_outputopts;
+ error = ip6_pcbopt(IPV6_HOPLIMIT,
+ (u_char *)&optval,
+ sizeof(optval),
+ optp,
+ privileged, uproto);
+ break;
+ }
+
+ case IPV6_RECVHOPLIMIT:
+ /* cannot mix with RFC2292 */
+ if (OPTBIT(IN6P_RFC2292)) {
+ error = EINVAL;
+ break;
+ }
+ OPTSET(IN6P_HOPLIMIT);
+ break;
+
+ case IPV6_RECVHOPOPTS:
+ /* cannot mix with RFC2292 */
+ if (OPTBIT(IN6P_RFC2292)) {
+ error = EINVAL;
+ break;
+ }
+ OPTSET(IN6P_HOPOPTS);
+ break;
+
+ case IPV6_RECVDSTOPTS:
+ /* cannot mix with RFC2292 */
+ if (OPTBIT(IN6P_RFC2292)) {
+ error = EINVAL;
+ break;
+ }
+ OPTSET(IN6P_DSTOPTS);
+ break;
+
+ case IPV6_RECVRTHDRDSTOPTS:
+ /* cannot mix with RFC2292 */
+ if (OPTBIT(IN6P_RFC2292)) {
+ error = EINVAL;
+ break;
+ }
+ OPTSET(IN6P_RTHDRDSTOPTS);
+ break;
+
+ case IPV6_RECVRTHDR:
+ /* cannot mix with RFC2292 */
+ if (OPTBIT(IN6P_RFC2292)) {
+ error = EINVAL;
+ break;
+ }
+ OPTSET(IN6P_RTHDR);
break;
case IPV6_FAITH:
OPTSET(IN6P_FAITH);
break;
+ case IPV6_RECVPATHMTU:
+ /*
+ * We ignore this option for TCP
+ * sockets.
+ * (rfc2292bis leaves this case
+ * unspecified.)
+ */
+ if (uproto != IPPROTO_TCP)
+ OPTSET(IN6P_MTU);
+ break;
+
case IPV6_V6ONLY:
/*
* make setsockopt(IPV6_V6ONLY)
@@ -1445,14 +1640,49 @@ do { \
else
in6p->in6p_vflag |= INP_IPV4;
break;
+ case IPV6_RECVTCLASS:
+ /* cannot mix with RFC2292 XXX */
+ if (OPTBIT(IN6P_RFC2292)) {
+ error = EINVAL;
+ break;
+ }
+ OPTSET(IN6P_TCLASS);
+ break;
+ case IPV6_AUTOFLOWLABEL:
+ OPTSET(IN6P_AUTOFLOWLABEL);
+ break;
+
}
break;
- case IPV6_PKTINFO:
- case IPV6_HOPLIMIT:
- case IPV6_HOPOPTS:
- case IPV6_DSTOPTS:
- case IPV6_RTHDR:
+ case IPV6_TCLASS:
+ case IPV6_DONTFRAG:
+ case IPV6_USE_MIN_MTU:
+ case IPV6_PREFER_TEMPADDR:
+ if (optlen != sizeof(optval)) {
+ error = EINVAL;
+ break;
+ }
+ error = sooptcopyin(sopt, &optval,
+ sizeof optval, sizeof optval);
+ if (error)
+ break;
+ {
+ struct ip6_pktopts **optp;
+ optp = &in6p->in6p_outputopts;
+ error = ip6_pcbopt(optname,
+ (u_char *)&optval,
+ sizeof(optval),
+ optp,
+ privileged, uproto);
+ break;
+ }
+
+ case IPV6_2292PKTINFO:
+ case IPV6_2292HOPLIMIT:
+ case IPV6_2292HOPOPTS:
+ case IPV6_2292DSTOPTS:
+ case IPV6_2292RTHDR:
/* RFC 2292 */
if (optlen != sizeof(int)) {
error = EINVAL;
@@ -1463,31 +1693,57 @@ do { \
if (error)
break;
switch (optname) {
- case IPV6_PKTINFO:
- OPTSET(IN6P_PKTINFO);
+ case IPV6_2292PKTINFO:
+ OPTSET2292(IN6P_PKTINFO);
break;
- case IPV6_HOPLIMIT:
- OPTSET(IN6P_HOPLIMIT);
+ case IPV6_2292HOPLIMIT:
+ OPTSET2292(IN6P_HOPLIMIT);
break;
- case IPV6_HOPOPTS:
+ case IPV6_2292HOPOPTS:
/*
* Check super-user privilege.
* See comments for IPV6_RECVHOPOPTS.
*/
if (!privileged)
return (EPERM);
- OPTSET(IN6P_HOPOPTS);
+ OPTSET2292(IN6P_HOPOPTS);
break;
- case IPV6_DSTOPTS:
+ case IPV6_2292DSTOPTS:
if (!privileged)
return (EPERM);
- OPTSET(IN6P_DSTOPTS|IN6P_RTHDRDSTOPTS); /* XXX */
+ OPTSET2292(IN6P_DSTOPTS|IN6P_RTHDRDSTOPTS); /* XXX */
break;
- case IPV6_RTHDR:
- OPTSET(IN6P_RTHDR);
+ case IPV6_2292RTHDR:
+ OPTSET2292(IN6P_RTHDR);
break;
}
break;
+ case IPV6_PKTINFO:
+ case IPV6_HOPOPTS:
+ case IPV6_RTHDR:
+ case IPV6_DSTOPTS:
+ case IPV6_RTHDRDSTOPTS:
+ case IPV6_NEXTHOP:
+ {
+ /* new advanced API (2292bis) */
+ u_char *optbuf;
+ int optlen;
+ struct ip6_pktopts **optp;
+
+ /* cannot mix with RFC2292 */
+ if (OPTBIT(IN6P_RFC2292)) {
+ error = EINVAL;
+ break;
+ }
+
+ optbuf = sopt->sopt_val;
+ optlen = sopt->sopt_valsize;
+ optp = &in6p->in6p_outputopts;
+ error = ip6_pcbopt(optname,
+ optbuf, optlen,
+ optp, privileged, uproto);
+ break;
+ }
#undef OPTSET
case IPV6_MULTICAST_IF:
@@ -1496,21 +1752,41 @@ do { \
case IPV6_JOIN_GROUP:
case IPV6_LEAVE_GROUP:
{
+ if (sopt->sopt_valsize > MLEN) {
+ error = EMSGSIZE;
+ break;
+ }
+ /* XXX */
+ }
+ /* FALLTHROUGH */
+ {
struct mbuf *m;
- if (sopt->sopt_valsize > MLEN) {
+ if (sopt->sopt_valsize > MCLBYTES) {
error = EMSGSIZE;
break;
}
/* XXX */
- MGET(m, sopt->sopt_td ? M_TRYWAIT : M_DONTWAIT, MT_HEADER);
+ MGET(m, sopt->sopt_td ? M_WAIT : M_DONTWAIT, MT_HEADER);
if (m == 0) {
error = ENOBUFS;
break;
}
+ if (sopt->sopt_valsize > MLEN) {
+ MCLGET(m, sopt->sopt_td ? M_WAIT : M_DONTWAIT);
+ if ((m->m_flags & M_EXT) == 0) {
+ m_free(m);
+ error = ENOBUFS;
+ break;
+ }
+ }
m->m_len = sopt->sopt_valsize;
error = sooptcopyin(sopt, mtod(m, char *),
m->m_len, m->m_len);
+ if (error) {
+ (void)m_free(m);
+ break;
+ }
error = ip6_setmoptions(sopt->sopt_name,
&in6p->in6p_moptions,
m);
@@ -1598,32 +1874,68 @@ do { \
case SOPT_GET:
switch (optname) {
+ case IPV6_2292PKTOPTIONS:
+#ifdef IPV6_PKTOPTIONS
case IPV6_PKTOPTIONS:
- if (in6p->in6p_options) {
- struct mbuf *m;
- m = m_copym(in6p->in6p_options,
- 0, M_COPYALL, M_TRYWAIT);
- error = soopt_mcopyout(sopt, m);
- if (error == 0)
- m_freem(m);
- } else
- sopt->sopt_valsize = 0;
+#endif
+ /*
+ * RFC3542 (effectively) deprecated the
+ * semantics of the 2292-style pktoptions.
+ * Since it was not reliable in nature (i.e.,
+ * applications had to expect the lack of some
+ * information after all), it would make sense
+ * to simplify this part by always returning
+ * empty data.
+ */
+ sopt->sopt_valsize = 0;
break;
+ case IPV6_RECVHOPOPTS:
+ case IPV6_RECVDSTOPTS:
+ case IPV6_RECVRTHDRDSTOPTS:
case IPV6_UNICAST_HOPS:
- case IPV6_CHECKSUM:
+ case IPV6_RECVPKTINFO:
+ case IPV6_RECVHOPLIMIT:
+ case IPV6_RECVRTHDR:
+ case IPV6_RECVPATHMTU:
case IPV6_FAITH:
case IPV6_V6ONLY:
case IPV6_PORTRANGE:
+ case IPV6_RECVTCLASS:
+ case IPV6_AUTOFLOWLABEL:
switch (optname) {
+ case IPV6_RECVHOPOPTS:
+ optval = OPTBIT(IN6P_HOPOPTS);
+ break;
+
+ case IPV6_RECVDSTOPTS:
+ optval = OPTBIT(IN6P_DSTOPTS);
+ break;
+
+ case IPV6_RECVRTHDRDSTOPTS:
+ optval = OPTBIT(IN6P_RTHDRDSTOPTS);
+ break;
+
case IPV6_UNICAST_HOPS:
optval = in6p->in6p_hops;
break;
- case IPV6_CHECKSUM:
- optval = in6p->in6p_cksum;
+ case IPV6_RECVPKTINFO:
+ optval = OPTBIT(IN6P_PKTINFO);
+ break;
+
+ case IPV6_RECVHOPLIMIT:
+ optval = OPTBIT(IN6P_HOPLIMIT);
+ break;
+
+ case IPV6_RECVRTHDR:
+ optval = OPTBIT(IN6P_RTHDR);
+ break;
+
+ case IPV6_RECVPATHMTU:
+ optval = OPTBIT(IN6P_MTU);
break;
case IPV6_FAITH:
@@ -1646,43 +1958,86 @@ do { \
optval = 0;
break;
}
+ case IPV6_RECVTCLASS:
+ optval = OPTBIT(IN6P_TCLASS);
+ break;
+
+ case IPV6_AUTOFLOWLABEL:
+ optval = OPTBIT(IN6P_AUTOFLOWLABEL);
+ break;
}
+ if (error)
+ break;
error = sooptcopyout(sopt, &optval,
sizeof optval);
break;
- case IPV6_PKTINFO:
- case IPV6_HOPLIMIT:
- case IPV6_HOPOPTS:
- case IPV6_RTHDR:
- case IPV6_DSTOPTS:
- if (optname == IPV6_HOPOPTS ||
- optname == IPV6_DSTOPTS ||
- !privileged)
- return (EPERM);
+ case IPV6_PATHMTU:
+ {
+ u_long pmtu = 0;
+ struct ip6_mtuinfo mtuinfo;
+ struct route_in6 *ro = (struct route_in6 *)&in6p->in6p_route;
+
+ if (!(so->so_state & SS_ISCONNECTED))
+ return (ENOTCONN);
+ /*
+ * XXX: we dot not consider the case of source
+ * routing, or optional information to specify
+ * the outgoing interface.
+ */
+ error = ip6_getpmtu(ro, NULL, NULL,
+ &in6p->in6p_faddr, &pmtu, NULL);
+ if (error)
+ break;
+ if (pmtu > IPV6_MAXPACKET)
+ pmtu = IPV6_MAXPACKET;
+
+ bzero(&mtuinfo, sizeof(mtuinfo));
+ mtuinfo.ip6m_mtu = (u_int32_t)pmtu;
+ optdata = (void *)&mtuinfo;
+ optdatalen = sizeof(mtuinfo);
+ error = sooptcopyout(sopt, optdata,
+ optdatalen);
+ break;
+ }
+
+ case IPV6_2292PKTINFO:
+ case IPV6_2292HOPLIMIT:
+ case IPV6_2292HOPOPTS:
+ case IPV6_2292RTHDR:
+ case IPV6_2292DSTOPTS:
switch (optname) {
- case IPV6_PKTINFO:
+ case IPV6_2292PKTINFO:
optval = OPTBIT(IN6P_PKTINFO);
break;
- case IPV6_HOPLIMIT:
+ case IPV6_2292HOPLIMIT:
optval = OPTBIT(IN6P_HOPLIMIT);
break;
- case IPV6_HOPOPTS:
- if (!privileged)
- return (EPERM);
+ case IPV6_2292HOPOPTS:
optval = OPTBIT(IN6P_HOPOPTS);
break;
- case IPV6_RTHDR:
+ case IPV6_2292RTHDR:
optval = OPTBIT(IN6P_RTHDR);
break;
- case IPV6_DSTOPTS:
- if (!privileged)
- return (EPERM);
+ case IPV6_2292DSTOPTS:
optval = OPTBIT(IN6P_DSTOPTS|IN6P_RTHDRDSTOPTS);
break;
}
error = sooptcopyout(sopt, &optval,
- sizeof optval);
+ sizeof optval);
+ break;
+ case IPV6_PKTINFO:
+ case IPV6_HOPOPTS:
+ case IPV6_RTHDR:
+ case IPV6_DSTOPTS:
+ case IPV6_RTHDRDSTOPTS:
+ case IPV6_NEXTHOP:
+ case IPV6_TCLASS:
+ case IPV6_DONTFRAG:
+ case IPV6_USE_MIN_MTU:
+ case IPV6_PREFER_TEMPADDR:
+ error = ip6_getpcbopt(in6p->in6p_outputopts,
+ optname, sopt);
break;
case IPV6_MULTICAST_IF:
@@ -1708,6 +2063,8 @@ do { \
size_t len = 0;
struct mbuf *m = NULL;
struct mbuf **mp = &m;
+ size_t ovalsize = sopt->sopt_valsize;
+ caddr_t oval = (caddr_t)sopt->sopt_val;
error = soopt_getm(sopt, &m); /* XXX */
if (error != NULL)
@@ -1715,6 +2072,8 @@ do { \
error = soopt_mcopyin(sopt, m); /* XXX */
if (error != NULL)
break;
+ sopt->sopt_valsize = ovalsize;
+ sopt->sopt_val = oval;
if (m) {
req = mtod(m, caddr_t);
len = m->m_len;
@@ -1751,7 +2110,7 @@ do { \
}
break;
}
- } else {
+ } else { /* level != IPPROTO_IPV6 */
error = EINVAL;
}
return (error);
@@ -1781,7 +2140,7 @@ ip6_pcbopts(pktopt, m, so, sopt)
opt->ip6po_rhinfo.ip6po_rhi_rthdr)
printf("ip6_pcbopts: all specified options are cleared.\n");
#endif
- ip6_clearpktopts(opt, 1, -1);
+ ip6_clearpktopts(opt, -1);
} else
opt = malloc(sizeof(*opt), M_IP6OPT, M_WAITOK);
*pktopt = NULL;
@@ -1798,8 +2157,9 @@ ip6_pcbopts(pktopt, m, so, sopt)
/* set options specified by user. */
if (td && !suser(td))
priv = 1;
- if ((error = ip6_setpktoptions(m, opt, priv, 1)) != 0) {
- ip6_clearpktopts(opt, 1, -1); /* XXX: discard all options */
+ if ((error = ip6_setpktoptions(m, opt, NULL, priv, 1,
+ so->so_proto->pr_protocol)) != 0) {
+ ip6_clearpktopts(opt, -1); /* XXX: discard all options */
free(opt, M_IP6OPT);
return (error);
}
@@ -1818,39 +2178,170 @@ init_ip6pktopts(opt)
bzero(opt, sizeof(*opt));
opt->ip6po_hlim = -1; /* -1 means default hop limit */
+ opt->ip6po_tclass = -1; /* -1 means default traffic class */
+ opt->ip6po_minmtu = IP6PO_MINMTU_MCASTONLY;
+ opt->ip6po_prefer_tempaddr = IP6PO_TEMPADDR_SYSTEM;
+}
+
+static int
+ip6_pcbopt(optname, buf, len, pktopt, priv, uproto)
+ int optname, len, priv;
+ u_char *buf;
+ struct ip6_pktopts **pktopt;
+ int uproto;
+{
+ struct ip6_pktopts *opt;
+
+ if (*pktopt == NULL) {
+ *pktopt = malloc(sizeof(struct ip6_pktopts), M_IP6OPT,
+ M_WAITOK);
+ init_ip6pktopts(*pktopt);
+ (*pktopt)->needfree = 1;
+ }
+ opt = *pktopt;
+
+ return (ip6_setpktoption(optname, buf, len, opt, priv, 1, 0, uproto));
+}
+
+static int
+ip6_getpcbopt(pktopt, optname, sopt)
+ struct ip6_pktopts *pktopt;
+ struct sockopt *sopt;
+ int optname;
+{
+ void *optdata = NULL;
+ int optdatalen = 0;
+ struct ip6_ext *ip6e;
+ int error = 0;
+ struct in6_pktinfo null_pktinfo;
+ int deftclass = 0, on;
+ int defminmtu = IP6PO_MINMTU_MCASTONLY;
+ int defpreftemp = IP6PO_TEMPADDR_SYSTEM;
+
+ switch (optname) {
+ case IPV6_PKTINFO:
+ if (pktopt && pktopt->ip6po_pktinfo)
+ optdata = (void *)pktopt->ip6po_pktinfo;
+ else {
+ /* XXX: we don't have to do this every time... */
+ bzero(&null_pktinfo, sizeof(null_pktinfo));
+ optdata = (void *)&null_pktinfo;
+ }
+ optdatalen = sizeof(struct in6_pktinfo);
+ break;
+ case IPV6_TCLASS:
+ if (pktopt && pktopt->ip6po_tclass >= 0)
+ optdata = (void *)&pktopt->ip6po_tclass;
+ else
+ optdata = (void *)&deftclass;
+ optdatalen = sizeof(int);
+ break;
+ case IPV6_HOPOPTS:
+ if (pktopt && pktopt->ip6po_hbh) {
+ optdata = (void *)pktopt->ip6po_hbh;
+ ip6e = (struct ip6_ext *)pktopt->ip6po_hbh;
+ optdatalen = (ip6e->ip6e_len + 1) << 3;
+ }
+ break;
+ case IPV6_RTHDR:
+ if (pktopt && pktopt->ip6po_rthdr) {
+ optdata = (void *)pktopt->ip6po_rthdr;
+ ip6e = (struct ip6_ext *)pktopt->ip6po_rthdr;
+ optdatalen = (ip6e->ip6e_len + 1) << 3;
+ }
+ break;
+ case IPV6_RTHDRDSTOPTS:
+ if (pktopt && pktopt->ip6po_dest1) {
+ optdata = (void *)pktopt->ip6po_dest1;
+ ip6e = (struct ip6_ext *)pktopt->ip6po_dest1;
+ optdatalen = (ip6e->ip6e_len + 1) << 3;
+ }
+ break;
+ case IPV6_DSTOPTS:
+ if (pktopt && pktopt->ip6po_dest2) {
+ optdata = (void *)pktopt->ip6po_dest2;
+ ip6e = (struct ip6_ext *)pktopt->ip6po_dest2;
+ optdatalen = (ip6e->ip6e_len + 1) << 3;
+ }
+ break;
+ case IPV6_NEXTHOP:
+ if (pktopt && pktopt->ip6po_nexthop) {
+ optdata = (void *)pktopt->ip6po_nexthop;
+ optdatalen = pktopt->ip6po_nexthop->sa_len;
+ }
+ break;
+ case IPV6_USE_MIN_MTU:
+ if (pktopt)
+ optdata = (void *)&pktopt->ip6po_minmtu;
+ else
+ optdata = (void *)&defminmtu;
+ optdatalen = sizeof(int);
+ break;
+ case IPV6_DONTFRAG:
+ if (pktopt && ((pktopt->ip6po_flags) & IP6PO_DONTFRAG))
+ on = 1;
+ else
+ on = 0;
+ optdata = (void *)&on;
+ optdatalen = sizeof(on);
+ break;
+ case IPV6_PREFER_TEMPADDR:
+ if (pktopt)
+ optdata = (void *)&pktopt->ip6po_prefer_tempaddr;
+ else
+ optdata = (void *)&defpreftemp;
+ optdatalen = sizeof(int);
+ break;
+ default: /* should not happen */
+#ifdef DIAGNOSTIC
+ panic("ip6_getpcbopt: unexpected option\n");
+#endif
+ return (ENOPROTOOPT);
+ }
+
+ error = sooptcopyout(sopt, optdata, optdatalen);
+
+ return (error);
}
void
-ip6_clearpktopts(pktopt, needfree, optname)
+ip6_clearpktopts(pktopt, optname)
struct ip6_pktopts *pktopt;
- int needfree, optname;
+ int optname;
{
- if (pktopt == NULL)
- return;
+ int needfree;
+
+ needfree = pktopt->needfree;
- if (optname == -1) {
+ if (optname == -1 || optname == IPV6_PKTINFO) {
if (needfree && pktopt->ip6po_pktinfo)
free(pktopt->ip6po_pktinfo, M_IP6OPT);
pktopt->ip6po_pktinfo = NULL;
}
- if (optname == -1)
+ if (optname == -1 || optname == IPV6_HOPLIMIT)
pktopt->ip6po_hlim = -1;
- if (optname == -1) {
+ if (optname == -1 || optname == IPV6_TCLASS)
+ pktopt->ip6po_tclass = -1;
+ if (optname == -1 || optname == IPV6_NEXTHOP) {
+ if (pktopt->ip6po_nextroute.ro_rt) {
+ RTFREE(pktopt->ip6po_nextroute.ro_rt);
+ pktopt->ip6po_nextroute.ro_rt = NULL;
+ }
if (needfree && pktopt->ip6po_nexthop)
free(pktopt->ip6po_nexthop, M_IP6OPT);
pktopt->ip6po_nexthop = NULL;
}
- if (optname == -1) {
+ if (optname == -1 || optname == IPV6_HOPOPTS) {
if (needfree && pktopt->ip6po_hbh)
free(pktopt->ip6po_hbh, M_IP6OPT);
pktopt->ip6po_hbh = NULL;
}
- if (optname == -1) {
+ if (optname == -1 || optname == IPV6_RTHDRDSTOPTS) {
if (needfree && pktopt->ip6po_dest1)
free(pktopt->ip6po_dest1, M_IP6OPT);
pktopt->ip6po_dest1 = NULL;
}
- if (optname == -1) {
+ if (optname == -1 || optname == IPV6_RTHDR) {
if (needfree && pktopt->ip6po_rhinfo.ip6po_rhi_rthdr)
free(pktopt->ip6po_rhinfo.ip6po_rhi_rthdr, M_IP6OPT);
pktopt->ip6po_rhinfo.ip6po_rhi_rthdr = NULL;
@@ -1859,7 +2350,7 @@ ip6_clearpktopts(pktopt, needfree, optname)
pktopt->ip6po_route.ro_rt = NULL;
}
}
- if (optname == -1) {
+ if (optname == -1 || optname == IPV6_DSTOPTS) {
if (needfree && pktopt->ip6po_dest2)
free(pktopt->ip6po_dest2, M_IP6OPT);
pktopt->ip6po_dest2 = NULL;
@@ -1893,8 +2384,11 @@ ip6_copypktopts(src, canwait)
if (dst == NULL && canwait == M_NOWAIT)
return (NULL);
bzero(dst, sizeof(*dst));
+ dst->needfree = 1;
dst->ip6po_hlim = src->ip6po_hlim;
+ dst->ip6po_tclass = src->ip6po_tclass;
+ dst->ip6po_flags = src->ip6po_flags;
if (src->ip6po_pktinfo) {
dst->ip6po_pktinfo = malloc(sizeof(*dst->ip6po_pktinfo),
M_IP6OPT, canwait);
@@ -1935,7 +2429,7 @@ ip6_freepcbopts(pktopt)
if (pktopt == NULL)
return;
- ip6_clearpktopts(pktopt, 1, -1);
+ ip6_clearpktopts(pktopt, -1);
free(pktopt, M_IP6OPT);
}
@@ -2300,17 +2794,33 @@ ip6_freemoptions(im6o)
* Set IPv6 outgoing packet options based on advanced API.
*/
int
-ip6_setpktoptions(control, opt, priv, needcopy)
+ip6_setpktoptions(control, opt, stickyopt, priv, needcopy, uproto)
struct mbuf *control;
- struct ip6_pktopts *opt;
- int priv, needcopy;
+ struct ip6_pktopts *opt, *stickyopt;
+ int priv, needcopy, uproto;
{
struct cmsghdr *cm = 0;
if (control == 0 || opt == 0)
return (EINVAL);
- init_ip6pktopts(opt);
+ if (stickyopt) {
+ /*
+ * If stickyopt is provided, make a local copy of the options
+ * for this particular packet, then override them by ancillary
+ * objects.
+ * XXX: need to gain a reference for the cached route of the
+ * next hop in case of the overriding.
+ */
+ *opt = *stickyopt;
+ if (opt->ip6po_nextroute.ro_rt) {
+ RT_LOCK(opt->ip6po_nextroute.ro_rt);
+ opt->ip6po_nextroute.ro_rt->rt_refcnt++;
+ RT_UNLOCK(opt->ip6po_nextroute.ro_rt);
+ }
+ } else
+ init_ip6pktopts(opt);
+ opt->needfree = needcopy;
/*
* XXX: Currently, we assume all the optional information is stored
@@ -2321,192 +2831,421 @@ ip6_setpktoptions(control, opt, priv, needcopy)
for (; control->m_len; control->m_data += CMSG_ALIGN(cm->cmsg_len),
control->m_len -= CMSG_ALIGN(cm->cmsg_len)) {
+ int error;
+
+ if (control->m_len < CMSG_LEN(0))
+ return (EINVAL);
+
cm = mtod(control, struct cmsghdr *);
if (cm->cmsg_len == 0 || cm->cmsg_len > control->m_len)
return (EINVAL);
if (cm->cmsg_level != IPPROTO_IPV6)
continue;
+ error = ip6_setpktoption(cm->cmsg_type, CMSG_DATA(cm),
+ cm->cmsg_len - CMSG_LEN(0), opt, priv, needcopy, 1, uproto);
+ if (error)
+ return (error);
+ }
+
+ return (0);
+}
+
+/*
+ * Set a particular packet option, as a sticky option or an ancillary data
+ * item. "len" can be 0 only when it's a sticky option.
+ * We have 4 cases of combination of "sticky" and "cmsg":
+ * "sticky=0, cmsg=0": impossible
+ * "sticky=0, cmsg=1": RFC2292 or rfc2292bis ancillary data
+ * "sticky=1, cmsg=0": rfc2292bis socket option
+ * "sticky=1, cmsg=1": RFC2292 socket option
+ */
+static int
+ip6_setpktoption(optname, buf, len, opt, priv, sticky, cmsg, uproto)
+ int optname, len, priv, sticky, cmsg, uproto;
+ u_char *buf;
+ struct ip6_pktopts *opt;
+{
+ int minmtupolicy, preftemp;
+
+ if (!sticky && !cmsg) {
+#ifdef DIAGNOSTIC
+ printf("ip6_setpktoption: impossible case\n");
+#endif
+ return (EINVAL);
+ }
+
+ /*
+ * IPV6_2292xxx is for backward compatibility to RFC2292, and should
+ * not be specified in the context of rfc2292bis. Conversely,
+ * rfc2292bis types should not be specified in the context of RFC2292.
+ */
+ if (!cmsg) {
+ switch (optname) {
+ case IPV6_2292PKTINFO:
+ case IPV6_2292HOPLIMIT:
+ case IPV6_2292NEXTHOP:
+ case IPV6_2292HOPOPTS:
+ case IPV6_2292DSTOPTS:
+ case IPV6_2292RTHDR:
+ case IPV6_2292PKTOPTIONS:
+ return (ENOPROTOOPT);
+ }
+ }
+ if (sticky && cmsg) {
+ switch (optname) {
+ case IPV6_PKTINFO:
+ case IPV6_HOPLIMIT:
+ case IPV6_NEXTHOP:
+ case IPV6_HOPOPTS:
+ case IPV6_DSTOPTS:
+ case IPV6_RTHDRDSTOPTS:
+ case IPV6_RTHDR:
+ case IPV6_USE_MIN_MTU:
+ case IPV6_DONTFRAG:
+ case IPV6_TCLASS:
+ case IPV6_PREFER_TEMPADDR: /* XXX: not an rfc2292bis option */
+ return (ENOPROTOOPT);
+ }
+ }
+
+ switch (optname) {
+ case IPV6_2292PKTINFO:
+ case IPV6_PKTINFO:
+ {
+ struct ifnet *ifp = NULL;
+ struct in6_pktinfo *pktinfo;
+
+ if (len != sizeof(struct in6_pktinfo))
+ return (EINVAL);
+
+ pktinfo = (struct in6_pktinfo *)buf;
+
/*
- * XXX should check if RFC2292 API is mixed with 2292bis API
+ * An application can clear any sticky IPV6_PKTINFO option by
+ * doing a "regular" setsockopt with ipi6_addr being
+ * in6addr_any and ipi6_ifindex being zero.
+ * [RFC 3542, Section 6]
*/
- switch (cm->cmsg_type) {
- case IPV6_PKTINFO:
- if (cm->cmsg_len != CMSG_LEN(sizeof(struct in6_pktinfo)))
- return (EINVAL);
- if (needcopy) {
- /* XXX: Is it really WAITOK? */
- opt->ip6po_pktinfo =
- malloc(sizeof(struct in6_pktinfo),
- M_IP6OPT, M_WAITOK);
- bcopy(CMSG_DATA(cm), opt->ip6po_pktinfo,
- sizeof(struct in6_pktinfo));
- } else
- opt->ip6po_pktinfo =
- (struct in6_pktinfo *)CMSG_DATA(cm);
- if (opt->ip6po_pktinfo->ipi6_ifindex &&
- IN6_IS_ADDR_LINKLOCAL(&opt->ip6po_pktinfo->ipi6_addr))
- opt->ip6po_pktinfo->ipi6_addr.s6_addr16[1] =
- htons(opt->ip6po_pktinfo->ipi6_ifindex);
-
- if (opt->ip6po_pktinfo->ipi6_ifindex > if_index
- || opt->ip6po_pktinfo->ipi6_ifindex < 0) {
+ if (optname == IPV6_PKTINFO && opt->ip6po_pktinfo &&
+ pktinfo->ipi6_ifindex == 0 &&
+ IN6_IS_ADDR_UNSPECIFIED(&pktinfo->ipi6_addr)) {
+ ip6_clearpktopts(opt, optname);
+ break;
+ }
+
+ if (uproto == IPPROTO_TCP && optname == IPV6_PKTINFO &&
+ sticky && !IN6_IS_ADDR_UNSPECIFIED(&pktinfo->ipi6_addr)) {
+ return (EINVAL);
+ }
+
+ /* validate the interface index if specified. */
+ if (pktinfo->ipi6_ifindex > if_index ||
+ pktinfo->ipi6_ifindex < 0) {
+ return (ENXIO);
+ }
+ if (pktinfo->ipi6_ifindex) {
+ ifp = ifnet_byindex(pktinfo->ipi6_ifindex);
+ if (ifp == NULL)
return (ENXIO);
- }
+ }
- /*
- * Check if the requested source address is indeed a
- * unicast address assigned to the node, and can be
- * used as the packet's source address.
- */
- if (!IN6_IS_ADDR_UNSPECIFIED(&opt->ip6po_pktinfo->ipi6_addr)) {
- struct in6_ifaddr *ia6;
- struct sockaddr_in6 sin6;
-
- bzero(&sin6, sizeof(sin6));
- sin6.sin6_len = sizeof(sin6);
- sin6.sin6_family = AF_INET6;
- sin6.sin6_addr =
- opt->ip6po_pktinfo->ipi6_addr;
- ia6 = (struct in6_ifaddr *)ifa_ifwithaddr(sin6tosa(&sin6));
- if (ia6 == NULL ||
- (ia6->ia6_flags & (IN6_IFF_ANYCAST |
- IN6_IFF_NOTREADY)) != 0)
- return (EADDRNOTAVAIL);
+ /*
+ * We store the address anyway, and let in6_selectsrc()
+ * validate the specified address. This is because ipi6_addr
+ * may not have enough information about its scope zone, and
+ * we may need additional information (such as outgoing
+ * interface or the scope zone of a destination address) to
+ * disambiguate the scope.
+ * XXX: the delay of the validation may confuse the
+ * application when it is used as a sticky option.
+ */
+ if (sticky) {
+ if (opt->ip6po_pktinfo == NULL) {
+ opt->ip6po_pktinfo = malloc(sizeof(*pktinfo),
+ M_IP6OPT, M_WAITOK);
}
- break;
+ bcopy(pktinfo, opt->ip6po_pktinfo, sizeof(*pktinfo));
+ } else
+ opt->ip6po_pktinfo = pktinfo;
+ break;
+ }
- case IPV6_HOPLIMIT:
- if (cm->cmsg_len != CMSG_LEN(sizeof(int)))
- return (EINVAL);
+ case IPV6_2292HOPLIMIT:
+ case IPV6_HOPLIMIT:
+ {
+ int *hlimp;
- opt->ip6po_hlim = *(int *)CMSG_DATA(cm);
- if (opt->ip6po_hlim < -1 || opt->ip6po_hlim > 255)
- return (EINVAL);
- break;
+ /*
+ * RFC 3542 deprecated the usage of sticky IPV6_HOPLIMIT
+ * to simplify the ordering among hoplimit options.
+ */
+ if (optname == IPV6_HOPLIMIT && sticky)
+ return (ENOPROTOOPT);
- case IPV6_NEXTHOP:
- if (!priv)
- return (EPERM);
+ if (len != sizeof(int))
+ return (EINVAL);
+ hlimp = (int *)buf;
+ if (*hlimp < -1 || *hlimp > 255)
+ return (EINVAL);
- if (cm->cmsg_len < sizeof(u_char) ||
- /* check if cmsg_len is large enough for sa_len */
- cm->cmsg_len < CMSG_LEN(*CMSG_DATA(cm)))
- return (EINVAL);
+ opt->ip6po_hlim = *hlimp;
+ break;
+ }
- if (needcopy) {
- opt->ip6po_nexthop =
- malloc(*CMSG_DATA(cm),
- M_IP6OPT, M_WAITOK);
- bcopy(CMSG_DATA(cm),
- opt->ip6po_nexthop,
- *CMSG_DATA(cm));
- } else
- opt->ip6po_nexthop =
- (struct sockaddr *)CMSG_DATA(cm);
- break;
+ case IPV6_TCLASS:
+ {
+ int tclass;
- case IPV6_HOPOPTS:
- {
- struct ip6_hbh *hbh;
- int hbhlen;
+ if (len != sizeof(int))
+ return (EINVAL);
+ tclass = *(int *)buf;
+ if (tclass < -1 || tclass > 255)
+ return (EINVAL);
- if (cm->cmsg_len < CMSG_LEN(sizeof(struct ip6_hbh)))
- return (EINVAL);
- hbh = (struct ip6_hbh *)CMSG_DATA(cm);
- hbhlen = (hbh->ip6h_len + 1) << 3;
- if (cm->cmsg_len != CMSG_LEN(hbhlen))
- return (EINVAL);
+ opt->ip6po_tclass = tclass;
+ break;
+ }
- if (needcopy) {
- opt->ip6po_hbh =
- malloc(hbhlen, M_IP6OPT, M_WAITOK);
- bcopy(hbh, opt->ip6po_hbh, hbhlen);
- } else
- opt->ip6po_hbh = hbh;
+ case IPV6_2292NEXTHOP:
+ case IPV6_NEXTHOP:
+ if (!priv)
+ return (EPERM);
+
+ if (len == 0) { /* just remove the option */
+ ip6_clearpktopts(opt, IPV6_NEXTHOP);
break;
}
- case IPV6_DSTOPTS:
+ /* check if cmsg_len is large enough for sa_len */
+ if (len < sizeof(struct sockaddr) || len < *buf)
+ return (EINVAL);
+
+ switch (((struct sockaddr *)buf)->sa_family) {
+ case AF_INET6:
{
- struct ip6_dest *dest, **newdest;
- int destlen;
+ struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)buf;
+#if 0
+ int error;
+#endif
- if (cm->cmsg_len < CMSG_LEN(sizeof(struct ip6_dest)))
+ if (sa6->sin6_len != sizeof(struct sockaddr_in6))
return (EINVAL);
- dest = (struct ip6_dest *)CMSG_DATA(cm);
- destlen = (dest->ip6d_len + 1) << 3;
- if (cm->cmsg_len != CMSG_LEN(destlen))
+
+ if (IN6_IS_ADDR_UNSPECIFIED(&sa6->sin6_addr) ||
+ IN6_IS_ADDR_MULTICAST(&sa6->sin6_addr)) {
return (EINVAL);
+ }
+#if 0
+ if ((error = scope6_check_id(sa6, ip6_use_defzone))
+ != 0) {
+ return (error);
+ }
+#endif
+ sa6->sin6_scope_id = 0; /* XXX */
+ break;
+ }
+ case AF_LINK: /* should eventually be supported */
+ default:
+ return (EAFNOSUPPORT);
+ }
+
+ /* turn off the previous option, then set the new option. */
+ ip6_clearpktopts(opt, IPV6_NEXTHOP);
+ if (sticky) {
+ opt->ip6po_nexthop = malloc(*buf, M_IP6OPT, M_WAITOK);
+ bcopy(buf, opt->ip6po_nexthop, *buf);
+ } else
+ opt->ip6po_nexthop = (struct sockaddr *)buf;
+ break;
+
+ case IPV6_2292HOPOPTS:
+ case IPV6_HOPOPTS:
+ {
+ struct ip6_hbh *hbh;
+ int hbhlen;
+
+ /*
+ * XXX: We don't allow a non-privileged user to set ANY HbH
+ * options, since per-option restriction has too much
+ * overhead.
+ */
+ if (!priv)
+ return (EPERM);
+
+ if (len == 0) {
+ ip6_clearpktopts(opt, IPV6_HOPOPTS);
+ break; /* just remove the option */
+ }
+
+ /* message length validation */
+ if (len < sizeof(struct ip6_hbh))
+ return (EINVAL);
+ hbh = (struct ip6_hbh *)buf;
+ hbhlen = (hbh->ip6h_len + 1) << 3;
+ if (len != hbhlen)
+ return (EINVAL);
+
+ /* turn off the previous option, then set the new option. */
+ ip6_clearpktopts(opt, IPV6_HOPOPTS);
+ if (sticky) {
+ opt->ip6po_hbh = malloc(hbhlen, M_IP6OPT, M_WAITOK);
+ bcopy(hbh, opt->ip6po_hbh, hbhlen);
+ } else
+ opt->ip6po_hbh = hbh;
+
+ break;
+ }
+
+ case IPV6_2292DSTOPTS:
+ case IPV6_DSTOPTS:
+ case IPV6_RTHDRDSTOPTS:
+ {
+ struct ip6_dest *dest, **newdest = NULL;
+ int destlen;
+
+ if (!priv) /* XXX: see the comment for IPV6_HOPOPTS */
+ return (EPERM);
+
+ if (len == 0) {
+ ip6_clearpktopts(opt, optname);
+ break; /* just remove the option */
+ }
- /*
- * The old advacned API is ambiguous on this
- * point. Our approach is to determine the
- * position based according to the existence
- * of a routing header. Note, however, that
- * this depends on the order of the extension
- * headers in the ancillary data; the 1st part
- * of the destination options header must
- * appear before the routing header in the
- * ancillary data, too.
- * RFC2292bis solved the ambiguity by
- * introducing separate cmsg types.
+ /* message length validation */
+ if (len < sizeof(struct ip6_dest))
+ return (EINVAL);
+ dest = (struct ip6_dest *)buf;
+ destlen = (dest->ip6d_len + 1) << 3;
+ if (len != destlen)
+ return (EINVAL);
+
+ /*
+ * Determine the position that the destination options header
+ * should be inserted; before or after the routing header.
+ */
+ switch (optname) {
+ case IPV6_2292DSTOPTS:
+ /*
+ * The old advacned API is ambiguous on this point.
+ * Our approach is to determine the position based
+ * according to the existence of a routing header.
+ * Note, however, that this depends on the order of the
+ * extension headers in the ancillary data; the 1st
+ * part of the destination options header must appear
+ * before the routing header in the ancillary data,
+ * too.
+ * RFC2292bis solved the ambiguity by introducing
+ * separate ancillary data or option types.
*/
if (opt->ip6po_rthdr == NULL)
newdest = &opt->ip6po_dest1;
else
newdest = &opt->ip6po_dest2;
+ break;
+ case IPV6_RTHDRDSTOPTS:
+ newdest = &opt->ip6po_dest1;
+ break;
+ case IPV6_DSTOPTS:
+ newdest = &opt->ip6po_dest2;
+ break;
+ }
- if (needcopy) {
- *newdest = malloc(destlen, M_IP6OPT, M_WAITOK);
- bcopy(dest, *newdest, destlen);
- } else
- *newdest = dest;
+ /* turn off the previous option, then set the new option. */
+ ip6_clearpktopts(opt, optname);
+ if (sticky) {
+ *newdest = malloc(destlen, M_IP6OPT, M_WAITOK);
+ bcopy(dest, *newdest, destlen);
+ } else
+ *newdest = dest;
- break;
+ break;
+ }
+
+ case IPV6_2292RTHDR:
+ case IPV6_RTHDR:
+ {
+ struct ip6_rthdr *rth;
+ int rthlen;
+
+ if (len == 0) {
+ ip6_clearpktopts(opt, IPV6_RTHDR);
+ break; /* just remove the option */
}
- case IPV6_RTHDR:
- {
- struct ip6_rthdr *rth;
- int rthlen;
+ /* message length validation */
+ if (len < sizeof(struct ip6_rthdr))
+ return (EINVAL);
+ rth = (struct ip6_rthdr *)buf;
+ rthlen = (rth->ip6r_len + 1) << 3;
+ if (len != rthlen)
+ return (EINVAL);
- if (cm->cmsg_len < CMSG_LEN(sizeof(struct ip6_rthdr)))
+ switch (rth->ip6r_type) {
+ case IPV6_RTHDR_TYPE_0:
+ if (rth->ip6r_len == 0) /* must contain one addr */
return (EINVAL);
- rth = (struct ip6_rthdr *)CMSG_DATA(cm);
- rthlen = (rth->ip6r_len + 1) << 3;
- if (cm->cmsg_len != CMSG_LEN(rthlen))
+ if (rth->ip6r_len % 2) /* length must be even */
return (EINVAL);
+ if (rth->ip6r_len / 2 != rth->ip6r_segleft)
+ return (EINVAL);
+ break;
+ default:
+ return (EINVAL); /* not supported */
+ }
- switch (rth->ip6r_type) {
- case IPV6_RTHDR_TYPE_0:
- /* must contain one addr */
- if (rth->ip6r_len == 0)
- return (EINVAL);
- /* length must be even */
- if (rth->ip6r_len % 2)
- return (EINVAL);
- if (rth->ip6r_len / 2 != rth->ip6r_segleft)
- return (EINVAL);
- break;
- default:
- return (EINVAL); /* not supported */
- }
+ /* turn off the previous option */
+ ip6_clearpktopts(opt, IPV6_RTHDR);
+ if (sticky) {
+ opt->ip6po_rthdr = malloc(rthlen, M_IP6OPT, M_WAITOK);
+ bcopy(rth, opt->ip6po_rthdr, rthlen);
+ } else
+ opt->ip6po_rthdr = rth;
- if (needcopy) {
- opt->ip6po_rthdr = malloc(rthlen, M_IP6OPT,
- M_WAITOK);
- bcopy(rth, opt->ip6po_rthdr, rthlen);
- } else
- opt->ip6po_rthdr = rth;
+ break;
+ }
- break;
+ case IPV6_USE_MIN_MTU:
+ if (len != sizeof(int))
+ return (EINVAL);
+ minmtupolicy = *(int *)buf;
+ if (minmtupolicy != IP6PO_MINMTU_MCASTONLY &&
+ minmtupolicy != IP6PO_MINMTU_DISABLE &&
+ minmtupolicy != IP6PO_MINMTU_ALL) {
+ return (EINVAL);
}
+ opt->ip6po_minmtu = minmtupolicy;
+ break;
- default:
- return (ENOPROTOOPT);
+ case IPV6_DONTFRAG:
+ if (len != sizeof(int))
+ return (EINVAL);
+
+ if (uproto == IPPROTO_TCP || *(int *)buf == 0) {
+ /*
+ * we ignore this option for TCP sockets.
+ * (rfc2292bis leaves this case unspecified.)
+ */
+ opt->ip6po_flags &= ~IP6PO_DONTFRAG;
+ } else
+ opt->ip6po_flags |= IP6PO_DONTFRAG;
+ break;
+
+ case IPV6_PREFER_TEMPADDR:
+ if (len != sizeof(int))
+ return (EINVAL);
+ preftemp = *(int *)buf;
+ if (preftemp != IP6PO_TEMPADDR_SYSTEM &&
+ preftemp != IP6PO_TEMPADDR_NOTPREFER &&
+ preftemp != IP6PO_TEMPADDR_PREFER) {
+ return (EINVAL);
}
- }
+ opt->ip6po_prefer_tempaddr = preftemp;
+ break;
+
+ default:
+ return (ENOPROTOOPT);
+ } /* end of switch */
return (0);
}
diff --git a/sys/netinet6/ip6_var.h b/sys/netinet6/ip6_var.h
index dc96ed7..641914d 100644
--- a/sys/netinet6/ip6_var.h
+++ b/sys/netinet6/ip6_var.h
@@ -128,6 +128,14 @@ struct ip6po_rhinfo {
#define ip6po_rthdr ip6po_rhinfo.ip6po_rhi_rthdr
#define ip6po_route ip6po_rhinfo.ip6po_rhi_route
+/* Nexthop related info */
+struct ip6po_nhinfo {
+ struct sockaddr *ip6po_nhi_nexthop;
+ struct route_in6 ip6po_nhi_route; /* Route to the nexthop */
+};
+#define ip6po_nexthop ip6po_nhinfo.ip6po_nhi_nexthop
+#define ip6po_nextroute ip6po_nhinfo.ip6po_nhi_route
+
struct ip6_pktopts {
struct mbuf *ip6po_m; /* Pointer to mbuf storing the data */
int ip6po_hlim; /* Hoplimit for outgoing packets */
@@ -135,8 +143,9 @@ struct ip6_pktopts {
/* Outgoing IF/address information */
struct in6_pktinfo *ip6po_pktinfo;
- struct sockaddr *ip6po_nexthop; /* Next-hop address */
-
+ /* Next-hop address information */
+ struct ip6po_nhinfo ip6po_nhinfo;
+
struct ip6_hbh *ip6po_hbh; /* Hop-by-Hop options header */
/* Destination options header (before a routing header) */
@@ -147,6 +156,29 @@ struct ip6_pktopts {
/* Destination options header (after a routing header) */
struct ip6_dest *ip6po_dest2;
+
+ int ip6po_tclass; /* traffic class */
+
+ int ip6po_minmtu; /* fragment vs PMTU discovery policy */
+#define IP6PO_MINMTU_MCASTONLY -1 /* default; send at min MTU for multicast*/
+#define IP6PO_MINMTU_DISABLE 0 /* always perform pmtu disc */
+#define IP6PO_MINMTU_ALL 1 /* always send at min MTU */
+
+ int ip6po_prefer_tempaddr; /* whether temporary addresses are
+ preferred as source address */
+#define IP6PO_TEMPADDR_SYSTEM -1 /* follow the system default */
+#define IP6PO_TEMPADDR_NOTPREFER 0 /* not prefer temporary address */
+#define IP6PO_TEMPADDR_PREFER 1 /* prefer temporary address */
+
+ int ip6po_flags;
+#if 0 /* parameters in this block is obsolete. do not reuse the values. */
+#define IP6PO_REACHCONF 0x01 /* upper-layer reachability confirmation. */
+#define IP6PO_MINMTU 0x02 /* use minimum MTU (IPV6_USE_MIN_MTU) */
+#endif
+#define IP6PO_DONTFRAG 0x04 /* disable fragmentation (IPV6_DONTFRAG) */
+#define IP6PO_USECOA 0x08 /* use care of address */
+
+ int needfree; /* members dynamically allocated */
};
/*
@@ -336,8 +368,9 @@ int ip6_output __P((struct mbuf *, struct ip6_pktopts *,
struct inpcb *));
int ip6_ctloutput __P((struct socket *, struct sockopt *));
void init_ip6pktopts __P((struct ip6_pktopts *));
-int ip6_setpktoptions __P((struct mbuf *, struct ip6_pktopts *, int, int));
-void ip6_clearpktopts __P((struct ip6_pktopts *, int, int));
+int ip6_setpktoptions __P((struct mbuf *, struct ip6_pktopts *,
+ struct ip6_pktopts *, int, int, int));
+void ip6_clearpktopts __P((struct ip6_pktopts *, int));
struct ip6_pktopts *ip6_copypktopts __P((struct ip6_pktopts *, int));
int ip6_optlen __P((struct inpcb *));
diff --git a/sys/netinet6/mld6.c b/sys/netinet6/mld6.c
index 15df24d..e80c251 100644
--- a/sys/netinet6/mld6.c
+++ b/sys/netinet6/mld6.c
@@ -125,7 +125,7 @@ mld6_init()
/* XXX: grotty hard coding... */
hbh_buf[2] = IP6OPT_PADN; /* 2 byte padding */
hbh_buf[3] = 0;
- hbh_buf[4] = IP6OPT_RTALERT;
+ hbh_buf[4] = IP6OPT_ROUTER_ALERT;
hbh_buf[5] = IP6OPT_RTALERT_LEN - 2;
bcopy((caddr_t)&rtalert_code, &hbh_buf[6], sizeof(u_int16_t));
diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c
index 7a74cdf..97b57e1 100644
--- a/sys/netinet6/nd6.c
+++ b/sys/netinet6/nd6.c
@@ -1323,7 +1323,7 @@ nd6_ioctl(cmd, data, ifp)
struct ifnet *ifp;
{
struct in6_drlist *drl = (struct in6_drlist *)data;
- struct in6_prlist *prl = (struct in6_prlist *)data;
+ struct in6_oprlist *oprl = (struct in6_oprlist *)data;
struct in6_ndireq *ndi = (struct in6_ndireq *)data;
struct in6_nbrinfo *nbi = (struct in6_nbrinfo *)data;
struct in6_ndifreq *ndif = (struct in6_ndifreq *)data;
@@ -1343,14 +1343,7 @@ nd6_ioctl(cmd, data, ifp)
dr = TAILQ_FIRST(&nd_defrouter);
while (dr && i < DRLSTSIZ) {
drl->defrouter[i].rtaddr = dr->rtaddr;
- if (IN6_IS_ADDR_LINKLOCAL(&drl->defrouter[i].rtaddr)) {
- /* XXX: need to this hack for KAME stack */
- drl->defrouter[i].rtaddr.s6_addr16[1] = 0;
- } else
- log(LOG_ERR,
- "default router list contains a "
- "non-linklocal address(%s)\n",
- ip6_sprintf(&drl->defrouter[i].rtaddr));
+ in6_clearscope(&drl->defrouter[i].rtaddr);
drl->defrouter[i].flags = dr->flags;
drl->defrouter[i].rtlifetime = dr->rtlifetime;
@@ -1364,50 +1357,46 @@ nd6_ioctl(cmd, data, ifp)
case SIOCGPRLST_IN6:
/*
* obsolete API, use sysctl under net.inet6.icmp6
+ *
+ * XXX the structure in6_prlist was changed in backward-
+ * incompatible manner. in6_oprlist is used for SIOCGPRLST_IN6,
+ * in6_prlist is used for nd6_sysctl() - fill_prlist().
*/
/*
* XXX meaning of fields, especialy "raflags", is very
* differnet between RA prefix list and RR/static prefix list.
* how about separating ioctls into two?
*/
- bzero(prl, sizeof(*prl));
+ bzero(oprl, sizeof(*oprl));
s = splnet();
pr = nd_prefix.lh_first;
while (pr && i < PRLSTSIZ) {
struct nd_pfxrouter *pfr;
int j;
- (void)in6_embedscope(&prl->prefix[i].prefix,
+ (void)in6_embedscope(&oprl->prefix[i].prefix,
&pr->ndpr_prefix, NULL, NULL);
- prl->prefix[i].raflags = pr->ndpr_raf;
- prl->prefix[i].prefixlen = pr->ndpr_plen;
- prl->prefix[i].vltime = pr->ndpr_vltime;
- prl->prefix[i].pltime = pr->ndpr_pltime;
- prl->prefix[i].if_index = pr->ndpr_ifp->if_index;
- prl->prefix[i].expire = pr->ndpr_expire;
+ oprl->prefix[i].raflags = pr->ndpr_raf;
+ oprl->prefix[i].prefixlen = pr->ndpr_plen;
+ oprl->prefix[i].vltime = pr->ndpr_vltime;
+ oprl->prefix[i].pltime = pr->ndpr_pltime;
+ oprl->prefix[i].if_index = pr->ndpr_ifp->if_index;
+ oprl->prefix[i].expire = pr->ndpr_expire;
pfr = pr->ndpr_advrtrs.lh_first;
j = 0;
while (pfr) {
if (j < DRLSTSIZ) {
-#define RTRADDR prl->prefix[i].advrtr[j]
+#define RTRADDR oprl->prefix[i].advrtr[j]
RTRADDR = pfr->router->rtaddr;
- if (IN6_IS_ADDR_LINKLOCAL(&RTRADDR)) {
- /* XXX: hack for KAME */
- RTRADDR.s6_addr16[1] = 0;
- } else
- log(LOG_ERR,
- "a router(%s) advertises "
- "a prefix with "
- "non-link local address\n",
- ip6_sprintf(&RTRADDR));
+ in6_clearscope(&RTRADDR);
#undef RTRADDR
}
j++;
pfr = pfr->pfr_next;
}
- prl->prefix[i].advrtrs = j;
- prl->prefix[i].origin = PR_ORIG_RA;
+ oprl->prefix[i].advrtrs = j;
+ oprl->prefix[i].origin = PR_ORIG_RA;
i++;
pr = pr->ndpr_next;
@@ -1419,16 +1408,16 @@ nd6_ioctl(cmd, data, ifp)
rpp = LIST_NEXT(rpp, rp_entry)) {
if (i >= PRLSTSIZ)
break;
- (void)in6_embedscope(&prl->prefix[i].prefix,
+ (void)in6_embedscope(&oprl->prefix[i].prefix,
&pr->ndpr_prefix, NULL, NULL);
- prl->prefix[i].raflags = rpp->rp_raf;
- prl->prefix[i].prefixlen = rpp->rp_plen;
- prl->prefix[i].vltime = rpp->rp_vltime;
- prl->prefix[i].pltime = rpp->rp_pltime;
- prl->prefix[i].if_index = rpp->rp_ifp->if_index;
- prl->prefix[i].expire = rpp->rp_expire;
- prl->prefix[i].advrtrs = 0;
- prl->prefix[i].origin = rpp->rp_origin;
+ oprl->prefix[i].raflags = rpp->rp_raf;
+ oprl->prefix[i].prefixlen = rpp->rp_plen;
+ oprl->prefix[i].vltime = rpp->rp_vltime;
+ oprl->prefix[i].pltime = rpp->rp_pltime;
+ oprl->prefix[i].if_index = rpp->rp_ifp->if_index;
+ oprl->prefix[i].expire = rpp->rp_expire;
+ oprl->prefix[i].advrtrs = 0;
+ oprl->prefix[i].origin = rpp->rp_origin;
i++;
}
}
diff --git a/sys/netinet6/nd6.h b/sys/netinet6/nd6.h
index 5762312..c60dc60 100644
--- a/sys/netinet6/nd6.h
+++ b/sys/netinet6/nd6.h
@@ -129,6 +129,24 @@ struct in6_defrouter {
u_short if_index;
};
+#ifdef _KERNEL
+struct in6_oprlist {
+ char ifname[IFNAMSIZ];
+ struct {
+ struct in6_addr prefix;
+ struct prf_ra raflags;
+ u_char prefixlen;
+ u_char origin;
+ u_long vltime;
+ u_long pltime;
+ u_long expire;
+ u_short if_index;
+ u_short advrtrs; /* number of advertisement routers */
+ struct in6_addr advrtr[DRLSTSIZ]; /* XXX: explicit limit */
+ } prefix[PRLSTSIZ];
+};
+#endif
+
struct in6_prlist {
char ifname[IFNAMSIZ];
struct {
@@ -150,9 +168,9 @@ struct in6_prefix {
struct prf_ra raflags;
u_char prefixlen;
u_char origin;
- u_long vltime;
- u_long pltime;
- u_long expire;
+ u_int32_t vltime;
+ u_int32_t pltime;
+ time_t expire;
u_int32_t flags;
int refcnt;
u_short if_index;
@@ -220,9 +238,6 @@ struct nd_defrouter {
u_char flags; /* flags on RA message */
u_short rtlifetime;
u_long expire;
- u_long advint; /* Mobile IPv6 addition (milliseconds) */
- u_long advint_expire; /* Mobile IPv6 addition */
- int advints_lost; /* Mobile IPv6 addition */
struct ifnet *ifp;
};
@@ -319,7 +334,7 @@ extern u_int32_t ip6_temp_valid_lifetime; /* seconds */
extern int ip6_temp_regen_advance; /* seconds */
union nd_opts {
- struct nd_opt_hdr *nd_opt_array[9]; /* max = home agent info */
+ struct nd_opt_hdr *nd_opt_array[13]; /* max = target address list */
struct {
struct nd_opt_hdr *zero;
struct nd_opt_hdr *src_lladdr;
@@ -328,8 +343,10 @@ union nd_opts {
struct nd_opt_rd_hdr *rh;
struct nd_opt_mtu *mtu;
struct nd_opt_hdr *six;
- struct nd_opt_advint *adv;
- struct nd_opt_hai *hai;
+ struct nd_opt_advinterval *adv;
+ struct nd_opt_homeagent_info *hai;
+ struct nd_opt_hdr *src_addrlist;
+ struct nd_opt_hdr *tgt_addrlist;
struct nd_opt_hdr *search; /* multiple opts */
struct nd_opt_hdr *last; /* multiple opts */
int done;
@@ -344,6 +361,8 @@ union nd_opts {
#define nd_opts_mtu nd_opt_each.mtu
#define nd_opts_adv nd_opt_each.adv
#define nd_opts_hai nd_opt_each.hai
+#define nd_opts_src_addrlist nd_opt_each.src_addrlist
+#define nd_opts_tgt_addrlist nd_opt_each.tgt_addrlist
#define nd_opts_search nd_opt_each.search
#define nd_opts_last nd_opt_each.last
#define nd_opts_done nd_opt_each.done
diff --git a/sys/netinet6/nd6_rtr.c b/sys/netinet6/nd6_rtr.c
index e5e61dd..858b5da 100644
--- a/sys/netinet6/nd6_rtr.c
+++ b/sys/netinet6/nd6_rtr.c
@@ -269,9 +269,6 @@ nd6_ra_input(m, off, icmp6len)
dr0.rtlifetime = ntohs(nd_ra->nd_ra_router_lifetime);
dr0.expire = time_second + dr0.rtlifetime;
dr0.ifp = ifp;
- dr0.advint = 0; /* Mobile IPv6 */
- dr0.advint_expire = 0; /* Mobile IPv6 */
- dr0.advints_lost = 0; /* Mobile IPv6 */
/* unspecified or not? (RFC 2461 6.3.4) */
if (advreachable) {
advreachable = ntohl(advreachable);
diff --git a/sys/netinet6/raw_ip6.c b/sys/netinet6/raw_ip6.c
index a4bd1c3..01f2440 100644
--- a/sys/netinet6/raw_ip6.c
+++ b/sys/netinet6/raw_ip6.c
@@ -331,10 +331,11 @@ rip6_output(m, va_alist)
struct inpcb *in6p;
u_int plen = m->m_pkthdr.len;
int error = 0;
- struct ip6_pktopts opt, *optp = 0;
+ struct ip6_pktopts opt, *stickyopt = NULL;
struct ifnet *oifp = NULL;
int type = 0, code = 0; /* for ICMPv6 output statistics only */
int priv = 0;
+ struct in6_addr *in6a;
va_list ap;
va_start(ap, m);
@@ -344,17 +345,21 @@ rip6_output(m, va_alist)
va_end(ap);
in6p = sotoin6pcb(so);
+ stickyopt = in6p->in6p_outputopts;
priv = 0;
if (so->so_cred->cr_uid == 0)
priv = 1;
dst = &dstsock->sin6_addr;
if (control) {
- if ((error = ip6_setpktoptions(control, &opt, priv, 0)) != 0)
+ if ((error = ip6_setpktoptions(control, &opt,
+ stickyopt, priv, 0,
+ so->so_proto->pr_protocol))
+ != 0) {
goto bad;
- optp = &opt;
- } else
- optp = in6p->in6p_outputopts;
+ }
+ in6p->in6p_outputopts = &opt;
+ }
/*
* For an ICMPv6 packet, we should know its type and code
@@ -393,7 +398,9 @@ rip6_output(m, va_alist)
* XXX Boundary check is assumed to be already done in
* ip6_setpktoptions().
*/
- if (optp && (pi = optp->ip6po_pktinfo) && pi->ipi6_ifindex) {
+ if (in6p->in6p_outputopts &&
+ (pi = in6p->in6p_outputopts->ip6po_pktinfo) &&
+ pi->ipi6_ifindex) {
ip6->ip6_dst.s6_addr16[1] = htons(pi->ipi6_ifindex);
oifp = ifnet_byindex(pi->ipi6_ifindex);
} else if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) &&
@@ -416,19 +423,16 @@ rip6_output(m, va_alist)
/*
* Source address selection.
*/
- {
- struct in6_addr *in6a;
-
- if ((in6a = in6_selectsrc(dstsock, optp, in6p->in6p_moptions,
- &in6p->in6p_route, &in6p->in6p_laddr, &error)) == 0) {
- if (error == 0)
- error = EADDRNOTAVAIL;
- goto bad;
- }
- ip6->ip6_src = *in6a;
- if (in6p->in6p_route.ro_rt)
- oifp = ifnet_byindex(in6p->in6p_route.ro_rt->rt_ifp->if_index);
+ if ((in6a = in6_selectsrc(dstsock, in6p->in6p_outputopts,
+ in6p->in6p_moptions, &in6p->in6p_route, &in6p->in6p_laddr,
+ &error)) == 0) {
+ if (error == 0)
+ error = EADDRNOTAVAIL;
+ goto bad;
}
+ ip6->ip6_src = *in6a;
+ if (in6p->in6p_route.ro_rt)
+ oifp = ifnet_byindex(in6p->in6p_route.ro_rt->rt_ifp->if_index);
ip6->ip6_flow = (ip6->ip6_flow & ~IPV6_FLOWINFO_MASK) |
(in6p->in6p_flowinfo & IPV6_FLOWINFO_MASK);
ip6->ip6_vfc = (ip6->ip6_vfc & ~IPV6_VERSION_MASK) |
@@ -466,7 +470,7 @@ rip6_output(m, va_alist)
*p = in6_cksum(m, ip6->ip6_nxt, sizeof(*ip6), plen);
}
- error = ip6_output(m, optp, &in6p->in6p_route, 0,
+ error = ip6_output(m, in6p->in6p_outputopts, &in6p->in6p_route, 0,
in6p->in6p_moptions, &oifp, in6p);
if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) {
if (oifp)
@@ -482,11 +486,9 @@ rip6_output(m, va_alist)
m_freem(m);
freectl:
- if (optp == &opt && optp->ip6po_rthdr && optp->ip6po_route.ro_rt)
- RTFREE(optp->ip6po_route.ro_rt);
if (control) {
- if (optp == &opt)
- ip6_clearpktopts(optp, 0, -1);
+ ip6_clearpktopts(in6p->in6p_outputopts, -1);
+ in6p->in6p_outputopts = stickyopt;
m_freem(control);
}
return (error);
diff --git a/sys/netinet6/route6.c b/sys/netinet6/route6.c
index 16f790c..c4b0f61 100644
--- a/sys/netinet6/route6.c
+++ b/sys/netinet6/route6.c
@@ -172,8 +172,7 @@ ip6_rthdr0(m, ip6, rh0)
index = addrs - rh0->ip6r0_segleft;
rh0->ip6r0_segleft--;
- /* note that ip6r0_addr does not exist in RFC2292bis */
- nextaddr = rh0->ip6r0_addr + index;
+ nextaddr = ((struct in6_addr *)(rh0 + 1)) + index;
/*
* reject invalid addresses. be proactive about malicious use of
diff --git a/sys/netinet6/udp6_output.c b/sys/netinet6/udp6_output.c
index a685f6a..f753a76 100644
--- a/sys/netinet6/udp6_output.c
+++ b/sys/netinet6/udp6_output.c
@@ -143,7 +143,8 @@ udp6_output(in6p, m, addr6, control, td)
if (td && !suser(td))
priv = 1;
if (control) {
- if ((error = ip6_setpktoptions(control, &opt, priv, 0)) != 0)
+ if ((error = ip6_setpktoptions(control, &opt, stickyopt, priv,
+ 0, IPPROTO_UDP)) != 0)
goto release;
in6p->in6p_outputopts = &opt;
}
@@ -304,7 +305,7 @@ release:
releaseopt:
if (control) {
- ip6_clearpktopts(in6p->in6p_outputopts, 0, -1);
+ ip6_clearpktopts(in6p->in6p_outputopts, -1);
in6p->in6p_outputopts = stickyopt;
m_freem(control);
}
diff --git a/usr.sbin/mld6query/Makefile b/usr.sbin/mld6query/Makefile
index 66f2a5e..7100520 100644
--- a/usr.sbin/mld6query/Makefile
+++ b/usr.sbin/mld6query/Makefile
@@ -18,6 +18,6 @@ PROG= mld6query
MAN= mld6query.8
SRCS= mld6.c
-CFLAGS+= -DINET6 -DIPSEC
+CFLAGS+= -DINET6 -DIPSEC -DUSE_RFC2292BIS
.include <bsd.prog.mk>
diff --git a/usr.sbin/mld6query/mld6.c b/usr.sbin/mld6query/mld6.c
index 9b0b30a..1f73203 100644
--- a/usr.sbin/mld6query/mld6.c
+++ b/usr.sbin/mld6query/mld6.c
@@ -1,4 +1,4 @@
-/* $KAME: mld6.c,v 1.11 2001/05/13 15:45:07 suz Exp $ */
+/* $KAME: mld6.c,v 1.15 2003/04/02 11:29:54 suz Exp $ */
/* $FreeBSD$ */
/*
@@ -34,6 +34,7 @@
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/time.h>
+#include <ifaddrs.h>
#include <unistd.h>
#include <signal.h>
@@ -51,9 +52,28 @@
#include <string.h>
#include <err.h>
+/* portability with older KAME headers */
+#ifndef MLD_LISTENER_QUERY
+#define MLD_LISTENER_QUERY MLD6_LISTENER_QUERY
+#define MLD_LISTENER_REPORT MLD6_LISTENER_REPORT
+#define MLD_LISTENER_DONE MLD6_LISTENER_DONE
+#define MLD_MTRACE_RESP MLD6_MTRACE_RESP
+#define MLD_MTRACE MLD6_MTRACE
+#define mld_hdr mld6_hdr
+#define mld_type mld6_type
+#define mld_code mld6_code
+#define mld_cksum mld6_cksum
+#define mld_maxdelay mld6_maxdelay
+#define mld_reserved mld6_reserved
+#define mld_addr mld6_addr
+#endif
+#ifndef IP6OPT_ROUTER_ALERT
+#define IP6OPT_ROUTER_ALERT IP6OPT_RTALERT
+#endif
+
struct msghdr m;
struct sockaddr_in6 dst;
-struct mld6_hdr mldh;
+struct mld_hdr mldh;
struct in6_addr maddr = IN6ADDR_ANY_INIT, any = IN6ADDR_ANY_INIT;
struct ipv6_mreq mreq;
u_short ifindex;
@@ -77,14 +97,14 @@ main(int argc, char *argv[])
u_int type;
int ch;
- type = MLD6_LISTENER_QUERY;
+ type = MLD_LISTENER_QUERY;
while ((ch = getopt(argc, argv, "dr")) != -1) {
switch (ch) {
case 'd':
- type = MLD6_LISTENER_DONE;
+ type = MLD_LISTENER_DONE;
break;
case 'r':
- type = MLD6_LISTENER_REPORT;
+ type = MLD_LISTENER_REPORT;
break;
default:
usage();
@@ -139,6 +159,8 @@ main(int argc, char *argv[])
(void)setitimer(ITIMER_REAL, &itimer, NULL);
FD_ZERO(&fdset);
+ if (s >= FD_SETSIZE)
+ errx(1, "descriptor too big");
for (;;) {
FD_SET(s, &fdset);
if ((i = select(s + 1, &fdset, NULL, NULL, NULL)) < 0)
@@ -156,10 +178,17 @@ make_msg(int index, struct in6_addr *addr, u_int type)
static struct iovec iov[2];
static u_char *cmsgbuf;
int cmsglen, hbhlen = 0;
+#ifdef USE_RFC2292BIS
+ void *hbhbuf = NULL, *optp = NULL;
+ int currentlen;
+#else
u_int8_t raopt[IP6OPT_RTALERT_LEN];
+#endif
struct in6_pktinfo *pi;
struct cmsghdr *cmsgp;
u_short rtalert_code = htons(IP6OPT_RTALERT_MLD);
+ struct ifaddrs *ifa, *ifap;
+ struct in6_addr src;
dst.sin6_len = sizeof(dst);
dst.sin6_family = AF_INET6;
@@ -177,13 +206,47 @@ make_msg(int index, struct in6_addr *addr, u_int type)
m.msg_iovlen = 1;
bzero(&mldh, sizeof(mldh));
- mldh.mld6_type = type & 0xff;
- mldh.mld6_maxdelay = htons(QUERY_RESPONSE_INTERVAL);
- mldh.mld6_addr = *addr;
+ mldh.mld_type = type & 0xff;
+ mldh.mld_maxdelay = htons(QUERY_RESPONSE_INTERVAL);
+ mldh.mld_addr = *addr;
+
+ /* MLD packet should be advertised from linklocal address */
+ getifaddrs(&ifa);
+ for (ifap = ifa; ifap; ifap = ifap->ifa_next) {
+ if (index != if_nametoindex(ifap->ifa_name))
+ continue;
+ if (ifap->ifa_addr->sa_family != AF_INET6)
+ continue;
+ if (!IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)
+ ifap->ifa_addr)->sin6_addr))
+ continue;
+ break;
+ }
+ if (ifap == NULL)
+ errx(1, "no linkocal address is available");
+ memcpy(&src, &((struct sockaddr_in6 *)ifap->ifa_addr)->sin6_addr,
+ sizeof(src));
+ freeifaddrs(ifa);
+#ifdef __KAME__
+ /* remove embedded ifindex */
+ src.s6_addr[2] = src.s6_addr[3] = 0;
+#endif
+
+#ifdef USE_RFC2292BIS
+ if ((hbhlen = inet6_opt_init(NULL, 0)) == -1)
+ errx(1, "inet6_opt_init(0) failed");
+ if ((hbhlen = inet6_opt_append(NULL, 0, hbhlen, IP6OPT_ROUTER_ALERT, 2,
+ 2, NULL)) == -1)
+ errx(1, "inet6_opt_append(0) failed");
+ if ((hbhlen = inet6_opt_finish(NULL, 0, hbhlen)) == -1)
+ errx(1, "inet6_opt_finish(0) failed");
+ cmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) + CMSG_SPACE(hbhlen);
+#else
hbhlen = sizeof(raopt);
cmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) +
inet6_option_space(hbhlen);
+#endif
if ((cmsgbuf = malloc(cmsglen)) == NULL)
errx(1, "can't allocate enough memory for cmsg");
@@ -196,23 +259,40 @@ make_msg(int index, struct in6_addr *addr, u_int type)
cmsgp->cmsg_type = IPV6_PKTINFO;
pi = (struct in6_pktinfo *)CMSG_DATA(cmsgp);
pi->ipi6_ifindex = index;
- memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr));
+ memcpy(&pi->ipi6_addr, &src, sizeof(pi->ipi6_addr));
/* specifiy to insert router alert option in a hop-by-hop opt hdr. */
cmsgp = CMSG_NXTHDR(&m, cmsgp);
+#ifdef USE_RFC2292BIS
+ cmsgp->cmsg_len = CMSG_LEN(hbhlen);
+ cmsgp->cmsg_level = IPPROTO_IPV6;
+ cmsgp->cmsg_type = IPV6_HOPOPTS;
+ hbhbuf = CMSG_DATA(cmsgp);
+ if ((currentlen = inet6_opt_init(hbhbuf, hbhlen)) == -1)
+ errx(1, "inet6_opt_init(len = %d) failed", hbhlen);
+ if ((currentlen = inet6_opt_append(hbhbuf, hbhlen, currentlen,
+ IP6OPT_ROUTER_ALERT, 2,
+ 2, &optp)) == -1)
+ errx(1, "inet6_opt_append(currentlen = %d, hbhlen = %d) failed",
+ currentlen, hbhlen);
+ (void)inet6_opt_set_val(optp, 0, &rtalert_code, sizeof(rtalert_code));
+ if ((currentlen = inet6_opt_finish(hbhbuf, hbhlen, currentlen)) == -1)
+ errx(1, "inet6_opt_finish(buf) failed");
+#else /* old advanced API */
if (inet6_option_init((void *)cmsgp, &cmsgp, IPV6_HOPOPTS))
errx(1, "inet6_option_init failed\n");
- raopt[0] = IP6OPT_RTALERT;
+ raopt[0] = IP6OPT_ROUTER_ALERT;
raopt[1] = IP6OPT_RTALERT_LEN - 2;
memcpy(&raopt[2], (caddr_t)&rtalert_code, sizeof(u_short));
if (inet6_option_append(cmsgp, raopt, 4, 0))
errx(1, "inet6_option_append failed\n");
+#endif
}
void
dump(int s)
{
int i;
- struct mld6_hdr *mld;
+ struct mld_hdr *mld;
u_char buf[1024];
struct sockaddr_in6 from;
int from_len = sizeof(from);
@@ -223,17 +303,17 @@ dump(int s)
&from_len)) < 0)
return;
- if (i < sizeof(struct mld6_hdr)) {
+ if (i < sizeof(struct mld_hdr)) {
printf("too short!\n");
return;
}
- mld = (struct mld6_hdr *)buf;
+ mld = (struct mld_hdr *)buf;
printf("from %s, ", inet_ntop(AF_INET6, &from.sin6_addr,
ntop_buf, sizeof(ntop_buf)));
- switch (mld->mld6_type) {
+ switch (mld->mld_type) {
case ICMP6_MEMBERSHIP_QUERY:
printf("type=Multicast Listener Query, ");
break;
@@ -244,7 +324,7 @@ dump(int s)
printf("type=Multicast Listener Done, ");
break;
}
- printf("addr=%s\n", inet_ntop(AF_INET6, &mld->mld6_addr,
+ printf("addr=%s\n", inet_ntop(AF_INET6, &mld->mld_addr,
ntop_buf, sizeof(ntop_buf)));
fflush(stdout);
@@ -252,7 +332,8 @@ dump(int s)
/* ARGSUSED */
void
-quit(int signum) {
+quit(int signum)
+{
mreq.ipv6mr_multiaddr = any;
mreq.ipv6mr_interface = ifindex;
if (setsockopt(s, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &mreq,
diff --git a/usr.sbin/traceroute6/Makefile b/usr.sbin/traceroute6/Makefile
index d4e63ac..ab4e52c 100644
--- a/usr.sbin/traceroute6/Makefile
+++ b/usr.sbin/traceroute6/Makefile
@@ -18,7 +18,7 @@ MAN= traceroute6.8
BINOWN= root
BINMODE= 4555
-CFLAGS+= -DINET6 -DIPSEC -DHAVE_POLL
+CFLAGS+= -DINET6 -DIPSEC -DUSE_RFC2292BIS -DHAVE_POLL
DPADD= ${LIBIPSEC}
LDADD= -lipsec
OpenPOWER on IntegriCloud