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