From 881c4fa39150df7d0de2dae7ae808f6a73cb199a Mon Sep 17 00:00:00 2001 From: ume Date: Fri, 24 Oct 2003 18:26:30 +0000 Subject: [PATCH] Switch Advanced Sockets API for IPv6 from RFC2292 to RFC3542 (aka RFC2292bis). Though I believe this commit doesn't break backward compatibility againt existing binaries, it breaks backward compatibility of API. Now, the applications which use Advanced Sockets API such as telnet, ping6, mld6query and traceroute6 use RFC3542 API. Obtained from: KAME --- contrib/telnet/telnet/commands.c | 25 +- lib/libc/net/Makefile.inc | 16 +- lib/libc/net/getaddrinfo.c | 452 +++++++---- lib/libc/net/inet6_opt_init.3 | 291 +++++++ lib/libc/net/inet6_rth_space.3 | 254 ++++++ lib/libc/net/ip6opt.c | 227 ++++++ lib/libc/net/rthdr.c | 536 ++++++++----- lib/libsdp/search.c | 1 + sbin/ping6/Makefile | 6 +- sbin/ping6/ping6.8 | 132 ++-- sbin/ping6/ping6.c | 381 ++++----- sys/netinet/icmp6.h | 51 +- sys/netinet/in.h | 1 + sys/netinet/in_pcb.h | 10 +- sys/netinet/ip6.h | 21 +- sys/netinet6/icmp6.c | 41 +- sys/netinet6/in6.h | 121 ++- sys/netinet6/in6_pcb.c | 3 +- sys/netinet6/in6_var.h | 5 +- sys/netinet6/ip6_input.c | 89 +-- sys/netinet6/ip6_output.c | 1267 +++++++++++++++++++++++------- sys/netinet6/ip6_var.h | 41 +- sys/netinet6/mld6.c | 2 +- sys/netinet6/nd6.c | 65 +- sys/netinet6/nd6.h | 37 +- sys/netinet6/nd6_rtr.c | 3 - sys/netinet6/raw_ip6.c | 48 +- sys/netinet6/route6.c | 3 +- sys/netinet6/udp6_output.c | 5 +- usr.sbin/mld6query/Makefile | 2 +- usr.sbin/mld6query/mld6.c | 113 ++- usr.sbin/traceroute6/Makefile | 2 +- 32 files changed, 3107 insertions(+), 1144 deletions(-) create mode 100644 lib/libc/net/inet6_opt_init.3 create mode 100644 lib/libc/net/inet6_rth_space.3 diff --git a/contrib/telnet/telnet/commands.c b/contrib/telnet/telnet/commands.c index 2635a20e490a..906a875ee2b3 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 01dc3b61e091..a00269e04bd1 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 e8c6d4fa912f..026909af40a8 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 #include #include +#ifdef INET6 +#include +#include +#include +#include +#include /* XXX */ +#endif /* INET6 */ #include #include -#include -#include -#include #include #include #include @@ -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,13 +512,63 @@ getaddrinfo(hostname, servname, hints, res) ai0 = *pai; - /* NULL hostname, or numeric hostname */ + /* + * 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; + + if (!MATCH_FAMILY(pai->ai_family, afd->a_af, 1)) + continue; + + if (pai->ai_family == PF_UNSPEC) + pai->ai_family = afd->a_af; + + 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]); + + if (!error && afailist[afd - afdl]) + found++; + } + if (found) { + numeric = 1; + goto globcopy; + } + + if (hostname == NULL) + ERR(EAI_NONAME); /* used to be EAI_NODATA */ + if (pai->ai_flags & AI_NUMERICHOST) + ERR(EAI_NONAME); + + /* + * hostname as alphabetical name. + * 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; - /* PF_UNSPEC entries are prepared for DNS queries only */ - if (ex->e_af == PF_UNSPEC) - continue; + 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; @@ -495,6 +577,23 @@ getaddrinfo(hostname, servname, hints, res) if (!MATCH(pai->ai_protocol, ex->e_protocol, WILD_PROTOCOL(ex))) continue; +#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; +#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) @@ -502,86 +601,93 @@ getaddrinfo(hostname, servname, hints, res) if (pai->ai_protocol == ANY && ex->e_protocol != ANY) pai->ai_protocol = ex->e_protocol; - if (hostname == NULL) - error = explore_null(pai, servname, &cur->ai_next); - else - error = explore_numeric_scope(pai, hostname, servname, &cur->ai_next); + /* + * if the servname does not match socktype/protocol, ignore it. + */ + if (get_portmatch(pai, servname) != 0) + continue; - if (error) - goto free; + 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 - * 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. - */ - for (ex = explore; ex->e_af >= 0; ex++) { - *pai = ai0; - - /* require exact match for family field */ - if (pai->ai_family != ex->e_af) - continue; - - if (!MATCH(pai->ai_socktype, ex->e_socktype, - WILD_SOCKTYPE(ex))) { - continue; - } - if (!MATCH(pai->ai_protocol, ex->e_protocol, - WILD_PROTOCOL(ex))) { - continue; - } - - if (pai->ai_socktype == ANY && ex->e_socktype != ANY) - pai->ai_socktype = ex->e_socktype; - if (pai->ai_protocol == ANY && ex->e_protocol != ANY) - pai->ai_protocol = ex->e_protocol; - - error = explore_fqdn(pai, hostname, servname, - &cur->ai_next); - - while (cur && cur->ai_next) - cur = cur->ai_next; - } - - /* XXX */ + /* 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; @@ -606,17 +711,6 @@ explore_null(pai, servname, res) sentinel.ai_next = NULL; cur = &sentinel; - /* - * filter out AFs that are not supported by the kernel - * XXX errno? - */ - s = _socket(pai->ai_family, SOCK_DGRAM, 0); - if (s < 0) { - if (errno != EMFILE) - return 0; - } else - _close(s); - /* * if the servname does not match socktype/protocol, ignore it. */ @@ -655,11 +749,12 @@ explore_null(pai, servname, res) * 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 @@ -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 @@ _yphostent(line, pai) *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 000000000000..65af709eaf8a --- /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 000000000000..1289b11b3eef --- /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 a26e45d3b83d..2f5491849b0d 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 abfe34a93ad8..d7b0077f606e 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 #include +/* + * 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; + struct cmsghdr *ch = (struct cmsghdr *)bp; + struct ip6_rthdr *rthdr; - rthdr = (struct ip6_rthdr *)CMSG_DATA(ch); + rthdr = (struct ip6_rthdr *)CMSG_DATA(ch); - ch->cmsg_level = IPPROTO_IPV6; - ch->cmsg_type = IPV6_RTHDR; + 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); + 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; + 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; - 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); -#endif - return(-1); - } + 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); - return(0); +#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 + 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); } +/* ARGSUSED */ int inet6_rthdr_lasthop(cmsg, flags) - struct cmsghdr *cmsg; - unsigned int flags; + struct cmsghdr *cmsg; + unsigned int flags; { - 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; - 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); - } + 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); + 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; + 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) { -#ifdef DEBUG - fprintf(stderr, "inet6_rthdr_getaddr: invalid size(%d)\n", - rt0->ip6r0_len); -#endif - return NULL; + 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 } - 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; - } - return &rt0->ip6r0_addr[idx - 1]; - } - default: -#ifdef DEBUG - fprintf(stderr, "inet6_rthdr_getaddr: unknown type(%d)\n", - rthdr->ip6r_type); -#endif - return NULL; - } + default: + return NULL; + } } int inet6_rthdr_getflags(cmsg, idx) - const struct cmsghdr *cmsg; - int idx; + const struct cmsghdr *cmsg; + int idx; { - 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; - int naddr; + 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) { -#ifdef DEBUG - fprintf(stderr, "inet6_rthdr_getflags: invalid size(%d)\n", - rt0->ip6r0_len); -#endif - return -1; + 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 */ } - 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; - } - 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; - } + default: + return (-1); + } +} + +/* + * 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 */ + } +} + +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_rth_add(void *bp, const struct in6_addr *addr) +{ + 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); +} + +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 */ + } + + return (0); +} + +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 */ + } +} + +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; + } } diff --git a/lib/libsdp/search.c b/lib/libsdp/search.c index ab495e32115f..c739824abb4d 100644 --- a/lib/libsdp/search.c +++ b/lib/libsdp/search.c @@ -29,6 +29,7 @@ * $FreeBSD$ */ +#include #include #include #include diff --git a/sbin/ping6/Makefile b/sbin/ping6/Makefile index 98378fc08dbc..4073a56debff 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 diff --git a/sbin/ping6/ping6.8 b/sbin/ping6/ping6.8 index 554bf2b04f9d..84a51edfb1f8 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 70e2221c6ae2..42c517cba95f 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 #include #include -#if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) #include -#endif #include #include #include #include #include +#ifdef HAVE_POLL_H +#include +#endif #ifdef IPSEC #include #include #endif -#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) #include -#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 2d290067008c..804ba6c48264 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 8aafb240f13e..039c1058080e 100644 --- a/sys/netinet/in.h +++ b/sys/netinet/in.h @@ -39,6 +39,7 @@ #include #include +#include #include /* Protocols common to RFC 1700, POSIX, and X/Open. */ diff --git a/sys/netinet/in_pcb.h b/sys/netinet/in_pcb.h index 96521e4f4a18..e8857bcfa356 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 e8dc1567caf6..ae66264625bb 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 aa963260ad39..30e534ff7fdc 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 92127f30f035..45481e91433c 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 */ @@ -482,6 +527,14 @@ struct in6_pktinfo { unsigned int ipi6_ifindex; /* send/recv interface index */ }; +/* + * 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_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 *, 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_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 a49bb593d6ff..eb623284f071 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 5a520fa3b605..b481b04d485f 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 54f252d18bdb..b8230978039e 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 51f5b7a9e064..b4859ece0600 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: { - struct mbuf *m; - if (sopt->sopt_valsize > MLEN) { error = EMSGSIZE; break; } /* XXX */ - MGET(m, sopt->sopt_td ? M_TRYWAIT : M_DONTWAIT, MT_HEADER); + } + /* FALLTHROUGH */ + { + struct mbuf *m; + + if (sopt->sopt_valsize > MCLBYTES) { + error = EMSGSIZE; + break; + } + /* XXX */ + 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; - if (optname == -1) { + needfree = pktopt->needfree; + + 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,193 +2831,422 @@ 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; - /* - * XXX should check if RFC2292 API is mixed with 2292bis API - */ - switch (cm->cmsg_type) { + 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: - 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) { - 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); - } - break; - case IPV6_HOPLIMIT: - if (cm->cmsg_len != CMSG_LEN(sizeof(int))) - return (EINVAL); - - opt->ip6po_hlim = *(int *)CMSG_DATA(cm); - if (opt->ip6po_hlim < -1 || opt->ip6po_hlim > 255) - return (EINVAL); - break; - case IPV6_NEXTHOP: - if (!priv) - return (EPERM); - - 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); - - 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_HOPOPTS: - { - struct ip6_hbh *hbh; - int hbhlen; + 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); + } + } - 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); + switch (optname) { + case IPV6_2292PKTINFO: + case IPV6_PKTINFO: + { + struct ifnet *ifp = NULL; + struct in6_pktinfo *pktinfo; - if (needcopy) { - opt->ip6po_hbh = - malloc(hbhlen, M_IP6OPT, M_WAITOK); - bcopy(hbh, opt->ip6po_hbh, hbhlen); - } else - opt->ip6po_hbh = hbh; + if (len != sizeof(struct in6_pktinfo)) + return (EINVAL); + + pktinfo = (struct in6_pktinfo *)buf; + + /* + * 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] + */ + if (optname == IPV6_PKTINFO && opt->ip6po_pktinfo && + pktinfo->ipi6_ifindex == 0 && + IN6_IS_ADDR_UNSPECIFIED(&pktinfo->ipi6_addr)) { + ip6_clearpktopts(opt, optname); break; } - case IPV6_DSTOPTS: + 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); + } + + /* + * 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); + } + bcopy(pktinfo, opt->ip6po_pktinfo, sizeof(*pktinfo)); + } else + opt->ip6po_pktinfo = pktinfo; + break; + } + + case IPV6_2292HOPLIMIT: + case IPV6_HOPLIMIT: + { + int *hlimp; + + /* + * RFC 3542 deprecated the usage of sticky IPV6_HOPLIMIT + * to simplify the ordering among hoplimit options. + */ + if (optname == IPV6_HOPLIMIT && sticky) + return (ENOPROTOOPT); + + if (len != sizeof(int)) + return (EINVAL); + hlimp = (int *)buf; + if (*hlimp < -1 || *hlimp > 255) + return (EINVAL); + + opt->ip6po_hlim = *hlimp; + break; + } + + case IPV6_TCLASS: + { + int tclass; + + if (len != sizeof(int)) + return (EINVAL); + tclass = *(int *)buf; + if (tclass < -1 || tclass > 255) + return (EINVAL); + + opt->ip6po_tclass = tclass; + break; + } + + case IPV6_2292NEXTHOP: + case IPV6_NEXTHOP: + if (!priv) + return (EPERM); + + if (len == 0) { /* just remove the option */ + ip6_clearpktopts(opt, IPV6_NEXTHOP); + break; + } + + /* 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))) - return (EINVAL); - dest = (struct ip6_dest *)CMSG_DATA(cm); - destlen = (dest->ip6d_len + 1) << 3; - if (cm->cmsg_len != CMSG_LEN(destlen)) + if (sa6->sin6_len != sizeof(struct sockaddr_in6)) return (EINVAL); - /* - * 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. + 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 */ + } + + /* 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; - - if (needcopy) { - *newdest = malloc(destlen, M_IP6OPT, M_WAITOK); - bcopy(dest, *newdest, destlen); - } else - *newdest = dest; - + break; + case IPV6_RTHDRDSTOPTS: + newdest = &opt->ip6po_dest1; + break; + case IPV6_DSTOPTS: + newdest = &opt->ip6po_dest2; break; } - case IPV6_RTHDR: - { - struct ip6_rthdr *rth; - int rthlen; + /* 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; - if (cm->cmsg_len < CMSG_LEN(sizeof(struct ip6_rthdr))) - return (EINVAL); - rth = (struct ip6_rthdr *)CMSG_DATA(cm); - rthlen = (rth->ip6r_len + 1) << 3; - if (cm->cmsg_len != CMSG_LEN(rthlen)) - return (EINVAL); - - 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 */ - } - - if (needcopy) { - opt->ip6po_rthdr = malloc(rthlen, M_IP6OPT, - M_WAITOK); - bcopy(rth, opt->ip6po_rthdr, rthlen); - } else - opt->ip6po_rthdr = rth; - - break; - } - - default: - return (ENOPROTOOPT); - } + 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 */ + } + + /* 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); + + switch (rth->ip6r_type) { + case IPV6_RTHDR_TYPE_0: + if (rth->ip6r_len == 0) /* must contain one addr */ + return (EINVAL); + 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 */ + } + + /* 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; + + 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; + + 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 dc96ed747b78..641914d453ae 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 15df24d45f39..e80c25176e81 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 7a74cdf997b4..97b57e130110 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 576231246dc9..c60dc60d6116 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 e5e61dd7e586..858b5daf0cf3 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 a4bd1c3e187b..01f244095300 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 16f790ca9ff6..c4b0f61b5728 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 a685f6a2dc85..f753a7609344 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 @@ udp6_output(in6p, m, addr6, control, td) 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 66f2a5eaaa36..7100520ad4d3 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 diff --git a/usr.sbin/mld6query/mld6.c b/usr.sbin/mld6query/mld6.c index 9b0b30ad644f..1f73203433e4 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 #include #include +#include #include #include @@ -51,9 +52,28 @@ #include #include +/* 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 d4e63ac3de04..ab4e52ce981a 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