From: Hasso Tepper Date: Thu, 4 Sep 2008 09:08:22 +0000 (+0000) Subject: The result of the "RFC3542 support" SoC project by Dashu Huang. X-Git-Tag: v2.1.1~500 X-Git-Url: https://gitweb.dragonflybsd.org/dragonfly.git/commitdiff_plain/5aa41e7ced77bd1f6ed0a000cfa79028e318494b The result of the "RFC3542 support" SoC project by Dashu Huang. --- diff --git a/lib/libc/net/Makefile.inc b/lib/libc/net/Makefile.inc index d8150f0f4f..1a0edd8e8d 100644 --- a/lib/libc/net/Makefile.inc +++ b/lib/libc/net/Makefile.inc @@ -1,6 +1,6 @@ # from @(#)Makefile.inc 8.2 (Berkeley) 9/5/93 # $FreeBSD: src/lib/libc/net/Makefile.inc,v 1.36.2.5 2002/07/29 18:33:18 ume Exp $ -# $DragonFly: src/lib/libc/net/Makefile.inc,v 1.4 2005/08/05 23:43:19 swildner Exp $ +# $DragonFly: src/lib/libc/net/Makefile.inc,v 1.5 2008/09/04 09:08:21 hasso Exp $ # machine-independent net sources .PATH: ${.CURDIR}/../libc/${MACHINE_ARCH}/net ${.CURDIR}/../libc/net @@ -33,7 +33,8 @@ MAN+= addr2ascii.3 byteorder.3 ethers.3 eui64.3 \ getifaddrs.3 getipnodebyname.3 \ getnameinfo.3 getnetent.3 getprotoent.3 getservent.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 \ rcmd.3 rcmdsh.3 resolver.3 # not installed: iso_addr.3 ns.3 @@ -65,11 +66,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/inet6_opt_init.3 b/lib/libc/net/inet6_opt_init.3 new file mode 100644 index 0000000000..29286c3cd2 --- /dev/null +++ b/lib/libc/net/inet6_opt_init.3 @@ -0,0 +1,338 @@ +.\" $KAME: inet6_opt_init.3,v 1.7 2004/12/27 05:08:23 itojun Exp $ +.\" +.\" Copyright (C) 2004 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: src/lib/libc/net/inet6_opt_init.3,v 1.10 2005/11/23 16:07:54 ru Exp $ +.\" $DragonFly: src/lib/libc/net/inet6_opt_init.3,v 1.1 2008/09/04 09:08:21 hasso Exp $ +.\" +.Dd December 23, 2004 +.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" "int offset" "void *val" "socklen_t vallen" +.\" +.Sh DESCRIPTION +Building and parsing the Hop-by-Hop and Destination options is +complicated. +The advanced sockets API defines a set of functions to +help applications create and manipulate Hop-by-Hop and Destination +options. +This man page describes the functions specified in +IETF Draft RFC3542. +These functions use 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 contained in the +.In netinet/in.h +header file. +.\" +.Ss inet6_opt_init +The +.Fn inet6_opt_init +function +returns the number of bytes needed for an empty +extension header, one without any options. +If the +.Fa extbuf +argument points to a valid section of memory +then the +.Fn inet6_opt_init +function also initializes the extension header's length field. +When attempting to initialize an extension buffer passed in the +.Fa extbuf +argument, +.Fa extlen +must be a positive multiple of 8 or else the function fails and +returns \-1 to the caller. +.\" +.Ss inet6_opt_append +The +.Fn inet6_opt_append +function can perform two different jobs. +When a valid +.Fa extbuf +argument is supplied it appends an option to the extension buffer and +returns the updated total length as well as a pointer to the newly +created option in +.Fa databufp . +If the value +of +.Fa extbuf +is +.Dv NULL +then the +.Fn inet6_opt_append +function only reports what the total length would +be if the option were actually appended. +The +.Fa len +and +.Fa align +arguments specify the length of the option and the required data +alignment which must be used when appending the option. +The +.Fa offset +argument should be the length returned by the +.Fn inet6_opt_init +function or a previous call to +.Fn inet6_opt_append . +.Pp +The +.Fa type +argument is the 8-bit option type. +.Pp +After +.Fn inet6_opt_append +has been called, the application can use the buffer pointed to by +.Fa databufp +directly, or use +.Fn inet6_opt_set_val +to specify the data to be contained in the option. +.Pp +Option types of +.Li 0 +and +.Li 1 +are reserved for the +.Li Pad1 +and +.Li PadN +options. +All other values from 2 through 255 may be used by applications. +.Pp +The length of the option data is contained in an 8-bit value and so +may contain any value from 0 through 255. +.Pp +The +.Fa align +parameter must have a value of 1, 2, 4, or 8 and cannot exceed the +value of +.Fa len . +The alignment values represent no alignment, 16 bit, 32 bit and 64 bit +alignments, respectively. +.\" +.Ss inet6_opt_finish +The +.Fn inet6_opt_finish +function +calculates the final padding necessary to make the extension header a +multiple of 8 bytes, as required by the IPv6 extension header +specification, and returns the extension header's updated total +length. +The +.Fa offset +argument should be the length returned by +.Fn inet6_opt_init +or +.Fn inet6_opt_append . +When +.Fa extbuf +is not +.Dv NULL +the function also sets up the appropriate padding bytes by inserting a +Pad1 or PadN option of the proper length. +.Pp +If the extension header is too small to contain the proper padding +then an error of \-1 is returned to the caller. +.\" +.Ss inet6_opt_set_val +The +.Fn inet6_opt_set_val +function inserts data items of various sizes into the data portion of +the option. +The +.Fa databuf +argument is a pointer to memory that was returned by the +.Fn inet6_opt_append +call and the +.Fa offset +argument specifies where the option should be placed in the +data buffer. +The +.Fa val +argument points to an area of memory containing the data to be +inserted into the extension header, and the +.Fa vallen +argument indicates how much data to copy. +.Pp +The caller should ensure that each field is aligned on its natural +boundaries as described in Appendix B of RFC2460. +.Pp +The function returns the offset for the next field which is calculated as +.Fa offset ++ +.Fa vallen +and is used when composing options with multiple fields. +.\" +.Ss inet6_opt_next +The +.Fn inet6_opt_next +function parses received extension headers. +The +.Fa extbuf +and +.Fa extlen +arguments specify the location and length of the extension header +being parsed. +The +.Fa offset +argument should either be zero, for the first option, or the length value +returned by a previous call to +.Fn inet6_opt_next +or +.Fn inet6_opt_find . +The return value specifies the position where to continue scanning the +extension buffer. +The option is returned in the arguments +.Fa typep , lenp , +and +.Fa databufp , +which +point to the 8-bit option type, the 8-bit option length and the option +data, respectively. +This function does not return any PAD1 or PADN options. +When an error occurs or there are no more options, the return +value is \-1. +.\" +.Ss inet6_opt_find +The +.Fn inet6_opt_find +function searches the extension buffer for a particular option type, +passed in through the +.Fa type +argument. +If the option is found then the +.Fa lenp +and +.Fa databufp +arguments are updated to point to the option's length and data, +respectively. +The +.Fa extbuf +and +.Fa extlen +arguments +must point to a valid extension buffer and give its length. +The +.Fa offset +argument can be used to search from a location anywhere in the +extension header. +.Ss inet6_opt_get_val +The +.Fn inet6_opt_get_val +function extracts data items of various sizes in the data portion of +the option. +The +.Fa databuf +is a pointer returned by the +.Fn inet6_opt_next +or +.Fn inet6_opt_find +functions. +The +.Fa val +argument points where the data will be extracted. +The +.Fa offset +argument specifies from where in the data portion of the option the +value should be extracted; the first byte of option data is specified +by an offset of zero. +.Pp +It is expected that each field is aligned on its natural boundaries as +described in Appendix B of RFC2460. +.Pp +The function returns the offset for the next field +by calculating +.Fa offset ++ +.Fa vallen +which can be used when extracting option content with multiple fields. +Robust receivers must verify alignment before calling this function. +.\" +.Sh RETURN VALUES +All the functions return +\-1 +on an error. +.\" +.Sh EXAMPLES +RFC3542 gives comprehensive examples in Section 23. +.Pp +KAME also provides examples in the +.Pa advapitest +directory 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 RFC3542 +.%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 STANDARDS +The functions are documented in +.Dq Advanced Sockets API for IPv6 +.Pq RFC3542 . +.\" +.Sh HISTORY +The implementation first appeared in KAME advanced networking kit. diff --git a/lib/libc/net/inet6_rth_space.3 b/lib/libc/net/inet6_rth_space.3 new file mode 100644 index 0000000000..9339a0b8c4 --- /dev/null +++ b/lib/libc/net/inet6_rth_space.3 @@ -0,0 +1,225 @@ +.\" $KAME: inet6_rth_space.3,v 1.7 2005/01/05 03:00:44 itojun Exp $ +.\" +.\" Copyright (C) 2004 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: src/lib/libc/net/inet6_rth_space.3,v 1.10 2005/07/31 03:30:44 keramida Exp $ +.\" $DragonFly: src/lib/libc/net/inet6_rth_space.3,v 1.1 2008/09/04 09:08:21 hasso Exp $ +.\" +.Dd December 24, 2004 +.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, RFC 3542, defines the functions that an +application calls to build and examine IPv6 Routing headers. +Routing headers are used to perform source routing in IPv6 networks. +The RFC uses the word +.Dq segments +to describe addresses and that is the term used here as well. +All of the functions are defined in the +.In netinet/in.h +header file. +The functions described in this manual page all operate +on routing header structures which are defined in +.In netinet/ip6.h +but which should not need to be modified outside the use of this API. +The size and shape of the route header structures may change, so using +the APIs is a more portable, long term, solution. +.Pp +The functions in the API are split into two groups, those that build a +routing header and those that parse a received routing header. +We will describe the builder functions followed by the parser functions. +.Ss inet6_rth_space +The +.Fn inet6_rth_space +function returns the number of bytes required to hold a Routing Header +of the type, specified in the +.Fa type +argument and containing the number of addresses specified in the +.Fa segments +argument. +When the type is +.Dv IPV6_RTHDR_TYPE_0 +the number of segments must be from 0 through 127. +Routing headers of type +.Dv IPV6_RTHDR_TYPE_2 +contain only one segment, and are only used with Mobile IPv6. +The return value from this function is the number of bytes required to +store the routing header. +If the value 0 is returned then either the +route header type was not recognized or another error occurred. +.Ss inet6_rth_init +The +.Fn inet6_rth_init +function initializes the pre-allocated buffer pointed to by +.Fa bp +to contain a routing header of the specified type The +.Fa bp_len +argument is used to verify that the buffer is large enough. +The caller must allocate the buffer pointed to by bp. +The necessary buffer size should be determined by calling +.Fn inet6_rth_space +described in the previous sections. +.Pp +The +.Fn inet6_rth_init +function returns a pointer to +.Fa bp +on success and +.Dv NULL +when there is an error. +.Ss inet6_rth_add +The +.Fn inet6_rth_add +function adds the IPv6 address pointed to by +.Fa addr +to the end of the routing header being constructed. +.Pp +A successful addition results in the function returning 0, otherwise +\-1 is returned. +.Ss inet6_rth_reverse +The +.Fn inet6_rth_reverse +function takes a routing header, pointed to by the +argument +.Fa in , +and writes a new routing header into the argument pointed to by +.Fa out . +The routing header at that sends datagrams along the reverse of that +route. +Both arguments are allowed to point to the same buffer meaning +that the reversal can occur in place. +.Pp +The return value of the function is 0 on success, or \-1 when +there is an error. +.\" +.Pp +The next set of functions operate on a routing header that the +application wants to parse. +In the usual case such a routing header +is received from the network, although these functions can also be +used with routing headers that the application itself created. +.Ss inet6_rth_segments +The +.Fn inet6_rth_segments +function returns the number of segments contained in the +routing header pointed to by +.Fa bp . +The return value is the number of segments contained in the routing +header, or \-1 if an error occurred. +It is not an error for 0 to be +returned as a routing header may contain 0 segments. +.\" +.Ss inet6_rth_getaddr +The +.Fn inet6_rth_getaddr +function is used to retrieve a single address from a routing header. +The +.Fa index +is the location in the routing header from which the application wants +to retrieve an address. +The +.Fa index +parameter must have a value between 0 and one less than the number of +segments present in the routing header. +The +.Fn inet6_rth_segments +function, described in the last section, should be used to determine +the total number of segments in the routing header. +The +.Fn inet6_rth_getaddr +function returns a pointer to an IPv6 address on success or +.Dv NULL +when an error has occurred. +.\" +.Sh EXAMPLES +RFC 3542 gives extensive examples in Section 21, Appendix B. +.Pp +KAME also provides examples in the advapitest directory of its kit. +.\" +.Sh DIAGNOSTICS +The +.Fn inet6_rth_space +and +.Fn inet6_rth_getaddr +functions return 0 on errors. +.Pp +The +.Fn inet6_rthdr_init +function returns +.Dv NULL +on error. +The +.Fn inet6_rth_add +and +.Fn inet6_rth_reverse +functions return 0 on success, or \-1 upon an error. +.\" +.Sh SEE ALSO +.Rs +.%A W. Stevens +.%A M. Thomas +.%A E. Nordmark +.%A T. Jinmei +.%T "Advanced Sockets API for IPv6" +.%N RFC 3542 +.%D May 2003 +.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. diff --git a/lib/libc/net/rthdr.c b/lib/libc/net/rthdr.c index 8e673e43c8..c4add50556 100644 --- a/lib/libc/net/rthdr.c +++ b/lib/libc/net/rthdr.c @@ -29,7 +29,7 @@ * SUCH DAMAGE. * * $FreeBSD: src/lib/libc/net/rthdr.c,v 1.2.2.1 2002/04/28 05:40:24 suz Exp $ - * $DragonFly: src/lib/libc/net/rthdr.c,v 1.5 2007/05/29 10:58:11 hasso Exp $ + * $DragonFly: src/lib/libc/net/rthdr.c,v 1.6 2008/09/04 09:08:21 hasso Exp $ */ #include @@ -42,265 +42,61 @@ #include #include +/* + * RFC5095 deprecated Type 0 routing header and we don't support any other + * routing header type yet. + */ size_t -inet6_rthdr_space(int type, int seg) +inet6_rthdr_space(int type __unused, int seg __unused) { - 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); -#endif - return(0); - } + return(0); /* type not suppported */ } struct cmsghdr * -inet6_rthdr_init(void *bp, int type) +inet6_rthdr_init(void *bp __unused, int type __unused) { - struct cmsghdr *ch = (struct cmsghdr *)bp; - struct ip6_rthdr *rthdr; - - rthdr = (struct ip6_rthdr *)CMSG_DATA(ch); - - ch->cmsg_level = IPPROTO_IPV6; - ch->cmsg_type = IPV6_RTHDR; - - switch(type) { - case IPV6_RTHDR_TYPE_0: - ch->cmsg_len = CMSG_LEN(sizeof(struct ip6_rthdr0) - sizeof(struct in6_addr)); - bzero(rthdr, sizeof(struct ip6_rthdr0)); - rthdr->ip6r_type = IPV6_RTHDR_TYPE_0; - return(ch); - default: -#ifdef DEBUG - fprintf(stderr, "inet6_rthdr_init: unknown type(%d)\n", type); -#endif - return(NULL); - } + return(NULL); /* type not suppported */ } int -inet6_rthdr_add(struct cmsghdr *cmsg, const struct in6_addr *addr, u_int flags) +inet6_rthdr_add(struct cmsghdr *cmsg __unused, const struct in6_addr *addr __unused, u_int flags __unused) { - struct ip6_rthdr *rthdr; - - rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg); - - switch(rthdr->ip6r_type) { - case IPV6_RTHDR_TYPE_0: - { - struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr; - if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT) { -#ifdef DEBUG - fprintf(stderr, "inet6_rthdr_add: unsupported flag(%d)\n", flags); -#endif - return(-1); - } - if (rt0->ip6r0_segleft == 23) { -#ifdef DEBUG - fprintf(stderr, "inet6_rthdr_add: segment overflow\n"); -#endif - return(-1); - } - if (flags == IPV6_RTHDR_STRICT) { - int c, b; - c = rt0->ip6r0_segleft / 8; - b = rt0->ip6r0_segleft % 8; - rt0->ip6r0_slmap[c] |= (1 << (7 - b)); - } - rt0->ip6r0_segleft++; - bcopy(addr, (caddr_t)rt0 + ((rt0->ip6r0_len + 1) << 3), - sizeof(struct in6_addr)); - rt0->ip6r0_len += sizeof(struct in6_addr) >> 3; - cmsg->cmsg_len = CMSG_LEN((rt0->ip6r0_len + 1) << 3); - break; - } - default: -#ifdef DEBUG - fprintf(stderr, "inet6_rthdr_add: unknown type(%d)\n", - rthdr->ip6r_type); -#endif - return(-1); - } - - return(0); + return(-1); /* type not suppported */ } int -inet6_rthdr_lasthop(struct cmsghdr *cmsg, unsigned int flags) +inet6_rthdr_lasthop(struct cmsghdr *cmsg __unused, unsigned int flags __unused) { - struct ip6_rthdr *rthdr; - - rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg); - - switch(rthdr->ip6r_type) { - case IPV6_RTHDR_TYPE_0: - { - struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr; - if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT) { -#ifdef DEBUG - fprintf(stderr, "inet6_rthdr_lasthop: unsupported flag(%d)\n", flags); -#endif - return(-1); - } - if (rt0->ip6r0_segleft > 23) { -#ifdef DEBUG - fprintf(stderr, "inet6_rthdr_add: segment overflow\n"); -#endif - return(-1); - } - if (flags == IPV6_RTHDR_STRICT) { - int c, b; - c = rt0->ip6r0_segleft / 8; - b = rt0->ip6r0_segleft % 8; - rt0->ip6r0_slmap[c] |= (1 << (7 - b)); - } - break; - } - default: -#ifdef DEBUG - fprintf(stderr, "inet6_rthdr_lasthop: unknown type(%d)\n", - rthdr->ip6r_type); -#endif - return(-1); - } - - return(0); + return (-1); /* type not suppported */ } -#if 0 int -inet6_rthdr_reverse(const struct cmsghdr *in, struct cmsghdr *out) +inet6_rthdr_reverse(const struct cmsghdr *in __unused, struct cmsghdr *out __unused) { -#ifdef DEBUG - fprintf(stderr, "inet6_rthdr_reverse: not implemented yet\n"); -#endif - return -1; + return -1; /* type not suppported */ } -#endif int -inet6_rthdr_segments(const struct cmsghdr *cmsg) +inet6_rthdr_segments(const struct cmsghdr *cmsg __unused) { - struct ip6_rthdr *rthdr; - - rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg); - - switch(rthdr->ip6r_type) { - case IPV6_RTHDR_TYPE_0: - { - struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr; - - if (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; - } - - 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; - } + return -1; /* type not suppported */ } struct in6_addr * -inet6_rthdr_getaddr(struct cmsghdr *cmsg, int idx) +inet6_rthdr_getaddr(struct cmsghdr *cmsg __unused, int idx __unused) { - struct ip6_rthdr *rthdr; - - rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg); - - switch(rthdr->ip6r_type) { - case IPV6_RTHDR_TYPE_0: - { - struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr; - int naddr; - - if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len) { -#ifdef DEBUG - fprintf(stderr, "inet6_rthdr_getaddr: invalid size(%d)\n", - rt0->ip6r0_len); -#endif - return NULL; - } - 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; - } + return NULL; /* type not suppported */ } int -inet6_rthdr_getflags(const struct cmsghdr *cmsg, int idx) +inet6_rthdr_getflags(const struct cmsghdr *cmsg __unused, int idx __unused) { - struct ip6_rthdr *rthdr; - - rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg); - - switch(rthdr->ip6r_type) { - case IPV6_RTHDR_TYPE_0: - { - struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr; - int naddr; - - if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len) { -#ifdef DEBUG - fprintf(stderr, "inet6_rthdr_getflags: invalid size(%d)\n", - rt0->ip6r0_len); -#endif - return -1; - } - 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; - } + return -1; /* type not suppported */ } /* * RFC3542 (2292bis) API */ - socklen_t inet6_rth_space(int type __unused, int segments __unused) { diff --git a/lib/libcompat/4.3/rexec.c b/lib/libcompat/4.3/rexec.c index 2c871f15c1..10b90686a9 100644 --- a/lib/libcompat/4.3/rexec.c +++ b/lib/libcompat/4.3/rexec.c @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * $FreeBSD: src/lib/libcompat/4.3/rexec.c,v 1.5.8.3 2000/11/22 13:36:00 ben Exp $ - * $DragonFly: src/lib/libcompat/4.3/rexec.c,v 1.3 2006/07/30 07:50:28 swildner Exp $ + * $DragonFly: src/lib/libcompat/4.3/rexec.c,v 1.4 2008/09/04 09:08:22 hasso Exp $ * * @(#)rexec.c 8.1 (Berkeley) 6/4/93 */ @@ -294,6 +294,147 @@ bad: return (-1); } +int +rexec_af(ahost, rport, name, pass, cmd, fd2p, af) + char **ahost; + int rport; + const char *name, *pass, *cmd; + int *fd2p; + sa_family_t af; +{ + struct sockaddr_storage sa2, from; + struct addrinfo hints, *res0; + const char *orig_name = name; + const char *orig_pass = pass; + static char *ahostbuf; + u_short port = 0; + int s, timo = 1, s3; + char c; + int gai; + char servbuff[NI_MAXSERV]; + + sprintf(servbuff, sizeof(servbuff), "%d", ntohs(rport)); + servbuff[sizeof(servbuff) - 1] = '\0'; + + memset(&hints, '\0', sizeof(hints)); + hints.ai_family = af; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_CANONNAME; + gai = getaddrinfo(*ahost, servbuff, &hints, &res0); + if (gai){ + /* XXX: set errno? */ + return -1; + } + + if (res0->ai_canonname){ + free (ahostbuf); + ahostbuf = strdup (res0->ai_canonname); + if (ahostbuf == NULL) { + perror ("rexec: strdup"); + return (-1); + } + *ahost = ahostbuf; + } else { + *ahost = NULL; + __set_errno (ENOENT); + return -1; + } + ruserpass(res0->ai_canonname, &name, &pass); +retry: + s = socket(res0->ai_family, res0->ai_socktype, 0); + if (s < 0) { + perror("rexec: socket"); + return (-1); + } + if (connect(s, res0->ai_addr, res0->ai_addrlen) < 0) { + if (errno == ECONNREFUSED && timo <= 16) { + (void) close(s); + sleep(timo); + timo *= 2; + goto retry; + } + perror(res0->ai_canonname); + return (-1); + } + if (fd2p == 0) { + (void) write(s, "", 1); + port = 0; + } else { + char num[32]; + int s2; + socklen_t sa2len; + + s2 = socket(res0->ai_family, res0->ai_socktype, 0); + if (s2 < 0) { + (void) close(s); + return (-1); + } + listen(s2, 1); + sa2len = sizeof (sa2); + if (getsockname(s2, (struct sockaddr *)&sa2, &sa2len) < 0) { + perror("getsockname"); + (void) close(s2); + goto bad; + } else if (sa2len != SA_LEN((struct sockaddr *)&sa2)) { + __set_errno(EINVAL); + (void) close(s2); + goto bad; + } + port = 0; + if (!getnameinfo((struct sockaddr *)&sa2, sa2len, + NULL, 0, servbuff, sizeof(servbuff), + NI_NUMERICSERV)) + port = atoi(servbuff); + (void) sprintf(num, "%u", port); + (void) write(s, num, strlen(num)+1); + { socklen_t len = sizeof (from); + s3 = accept(s2, (struct sockaddr *)&from, + &len); + close(s2); + if (s3 < 0) { + perror("accept"); + port = 0; + goto bad; + } + } + *fd2p = s3; + } + + (void) write(s, name, strlen(name) + 1); + /* should public key encypt the password here */ + (void) write(s, pass, strlen(pass) + 1); + (void) write(s, cmd, strlen(cmd) + 1); + + /* We don't need the memory allocated for the name and the password + in ruserpass anymore. */ + if (name != orig_name) + free ((char *) name); + if (pass != orig_pass) + free ((char *) pass); + + if (read(s, &c, 1) != 1) { + perror(*ahost); + goto bad; + } + if (c != 0) { + while (read(s, &c, 1) == 1) { + (void) write(2, &c, 1); + if (c == '\n') + break; + } + goto bad; + } + freeaddrinfo(res0); + return (s); +bad: + if (port) + (void) close(*fd2p); + (void) close(s); + freeaddrinfo(res0); + return (-1); +} + + int rexec(ahost, rport, name, pass, cmd, fd2p) char **ahost; diff --git a/sbin/ping6/Makefile b/sbin/ping6/Makefile index 996e4b1b1f..82a504e7d7 100644 --- a/sbin/ping6/Makefile +++ b/sbin/ping6/Makefile @@ -1,10 +1,10 @@ # $FreeBSD: src/sbin/ping6/Makefile,v 1.1.2.4 2002/03/08 09:18:59 ume Exp $ -# $DragonFly: src/sbin/ping6/Makefile,v 1.4 2006/10/17 00:55:43 pavalos Exp $ +# $DragonFly: src/sbin/ping6/Makefile,v 1.5 2008/09/04 09:08:22 hasso Exp $ PROG= ping6 MAN= ping6.8 -CFLAGS+=-DINET6 -DIPSEC +CFLAGS+=-DINET6 -DIPSEC -DUSE_RFC3542 BINOWN= root BINMODE=4555 diff --git a/sbin/ping6/ping6.c b/sbin/ping6/ping6.c index 02514233e7..058ae56ded 100644 --- a/sbin/ping6/ping6.c +++ b/sbin/ping6/ping6.c @@ -31,7 +31,7 @@ * @(#) Copyright (c) 1989, 1993 The Regents of the University of California. All rights reserved. * @(#)ping.c 8.1 (Berkeley) 6/5/93 * $FreeBSD: src/sbin/ping6/ping6.c,v 1.4.2.10 2002/12/09 03:04:44 suz Exp $ - * $DragonFly: src/sbin/ping6/ping6.c,v 1.8 2005/03/05 22:27:08 cpressey Exp $ + * $DragonFly: src/sbin/ping6/ping6.c,v 1.9 2008/09/04 09:08:22 hasso Exp $ */ /* BSDI ping.c,v 2.3 1996/01/21 17:56:50 jch Exp */ @@ -296,15 +296,17 @@ main(int argc, char **argv) int sockbufsize = 0; int usepktinfo = 0; struct in6_pktinfo *pktinfo = NULL; -#ifdef USE_RFC2292BIS +#ifdef USE_RFC3542 struct ip6_rthdr *rthdr = NULL; #endif #ifdef IPSEC_POLICY_IPSEC char *policy_in = NULL; char *policy_out = NULL; +#endif +#ifdef IPV6_USE_MIN_MTU + int mflag = 0; #endif double intval; - size_t rthlen; /* just to be sure */ memset(&smsghdr, 0, sizeof(smsghdr)); @@ -440,7 +442,7 @@ main(int argc, char **argv) break; case 'm': #ifdef IPV6_USE_MIN_MTU - options |= F_NOMINMTU; + mflag++; break; #else errx(1, "-%c is not supported on this platform", ch); @@ -532,19 +534,9 @@ main(int argc, char **argv) usage(); /*NOTREACHED*/ } - if (argc > 1) { -#ifdef IPV6_RECVRTHDR /* 2292bis */ - rthlen = CMSG_SPACE(inet6_rth_space(IPV6_RTHDR_TYPE_0, - argc - 1)); -#else /* RFC2292 */ - rthlen = inet6_rthdr_space(IPV6_RTHDR_TYPE_0, argc - 1); -#endif - if (rthlen == 0) { - errx(1, "too many intermediate hops"); - /*NOTREACHED*/ - } - ip6optlen += rthlen; + errx(1, "too many arguments"); + /*NOTREACHED*/ } if (options & F_NIGROUP) { @@ -669,8 +661,8 @@ main(int argc, char **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)"); @@ -818,60 +810,6 @@ main(int argc, char **argv) scmsgp = CMSG_NXTHDR(&smsghdr, scmsgp); } #endif - - if (argc > 1) { /* some intermediate addrs are specified */ - int hops, error; -#ifdef USE_RFC2292BIS - int rthdrlen; -#endif - -#ifdef USE_RFC2292BIS - rthdrlen = inet6_rth_space(IPV6_RTHDR_TYPE_0, argc - 1); - scmsgp->cmsg_len = CMSG_LEN(rthdrlen); - scmsgp->cmsg_level = IPPROTO_IPV6; - scmsgp->cmsg_type = IPV6_RTHDR; - rthdr = (struct ip6_rthdr *)CMSG_DATA(scmsgp); - rthdr = inet6_rth_init((void *)rthdr, rthdrlen, - IPV6_RTHDR_TYPE_0, argc - 1); - if (rthdr == NULL) - errx(1, "can't initialize rthdr"); -#else /* old advanced API */ - if ((scmsgp = (struct cmsghdr *)inet6_rthdr_init(scmsgp, - IPV6_RTHDR_TYPE_0)) == 0) - errx(1, "can't initialize rthdr"); -#endif /* USE_RFC2292BIS */ - - for (hops = 0; hops < argc - 1; hops++) { - struct addrinfo *iaip; - - if ((error = getaddrinfo(argv[hops], NULL, &hints, - &iaip))) - errx(1, "%s", gai_strerror(error)); - if (SIN6(iaip->ai_addr)->sin6_family != AF_INET6) - errx(1, - "bad addr family of an intermediate addr"); - -#ifdef USE_RFC2292BIS - if (inet6_rth_add(rthdr, - &(SIN6(iaip->ai_addr))->sin6_addr)) - errx(1, "can't add an intermediate node"); -#else /* old advanced API */ - if (inet6_rthdr_add(scmsgp, - &(SIN6(iaip->ai_addr))->sin6_addr, - IPV6_RTHDR_LOOSE)) - errx(1, "can't add an intermediate node"); -#endif /* USE_RFC2292BIS */ - freeaddrinfo(iaip); - } - -#ifndef USE_RFC2292BIS - if (inet6_rthdr_lasthop(scmsgp, IPV6_RTHDR_LOOSE)) - errx(1, "can't set the last flag"); -#endif - - scmsgp = CMSG_NXTHDR(&smsghdr, scmsgp); - } - { /* * source selection @@ -886,17 +824,19 @@ main(int argc, char **argv) src.sin6_port = ntohs(DUMMY_PORT); src.sin6_scope_id = dst.sin6_scope_id; -#ifdef USE_RFC2292BIS +#ifdef USE_RFC3542 if (pktinfo && setsockopt(dummy, IPPROTO_IPV6, IPV6_PKTINFO, (void *)pktinfo, sizeof(*pktinfo))) 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, (void *)rthdr, (rthdr->ip6r_len + 1) << 3)) @@ -1664,7 +1604,7 @@ pr_exthdrs(struct msghdr *mhdr) } } -#ifdef USE_RFC2292BIS +#ifdef USE_RFC3542 void pr_ip6opt(void *extbuf) { @@ -1715,7 +1655,7 @@ pr_ip6opt(void *extbuf) } return; } -#else /* !USE_RFC2292BIS */ +#else /* !USE_RFC3542 */ /* ARGSUSED */ void pr_ip6opt(void *extbuf __unused) @@ -1723,9 +1663,9 @@ pr_ip6opt(void *extbuf __unused) putchar('\n'); return; } -#endif /* USE_RFC2292BIS */ +#endif /* USE_RFC3542 */ -#ifdef USE_RFC2292BIS +#ifdef USE_RFC3542 void pr_rthdr(void *extbuf) { @@ -1737,29 +1677,13 @@ pr_rthdr(void *extbuf) /* print fixed part of the header */ printf("nxt %u, len %u (%d bytes), type %u, ", rh->ip6r_nxt, rh->ip6r_len, (rh->ip6r_len + 1) << 3, rh->ip6r_type); - if ((segments = inet6_rth_segments(extbuf)) >= 0) - printf("%d segments, ", segments); - else - printf("segments unknown, "); + printf("segments unknown, "); printf("%d left\n", rh->ip6r_segleft); - - for (i = 0; i < segments; i++) { - in6 = inet6_rth_getaddr(extbuf, i); - if (in6 == NULL) - printf(" [%d]\n", i); - else { - if (!inet_ntop(AF_INET6, in6, ntopbuf, - sizeof(ntopbuf))) - strncpy(ntopbuf, "?", sizeof(ntopbuf)); - printf(" [%d]%s\n", i, ntopbuf); - } - } - return; } -#else /* !USE_RFC2292BIS */ +#else /* !USE_RFC3542 */ /* ARGSUSED */ void pr_rthdr(void *extbuf __unused) @@ -1767,7 +1691,7 @@ pr_rthdr(void *extbuf __unused) putchar('\n'); return; } -#endif /* USE_RFC2292BIS */ +#endif /* USE_RFC3542 */ int pr_bitrange(u_int32_t v, int ss, int ii) diff --git a/sys/net/pf/pf_norm.c b/sys/net/pf/pf_norm.c index 77b020e784..b02b364397 100644 --- a/sys/net/pf/pf_norm.c +++ b/sys/net/pf/pf_norm.c @@ -1,7 +1,7 @@ /* $FreeBSD: src/sys/contrib/pf/net/pf_norm.c,v 1.10 2004/08/14 15:32:40 dwmalone Exp $ */ /* $OpenBSD: pf_norm.c,v 1.80.2.1 2004/04/30 21:46:33 brad Exp $ */ /* add $OpenBSD: pf_norm.c,v 1.87 2004/05/11 07:34:11 dhartmei Exp $ */ -/* $DragonFly: src/sys/net/pf/pf_norm.c,v 1.9 2007/08/11 18:53:31 dillon Exp $ */ +/* $DragonFly: src/sys/net/pf/pf_norm.c,v 1.10 2008/09/04 09:08:22 hasso Exp $ */ /* * Copyright (c) 2004 The DragonFly Project. All rights reserved. @@ -69,42 +69,6 @@ /* * XXX: This should go to netinet/ip6.h (KAME) */ -/* IPv6 options: common part */ -struct ip6_opt { - u_int8_t ip6o_type; - u_int8_t ip6o_len; -} __packed; - -/* Jumbo Payload Option */ -struct ip6_opt_jumbo { - u_int8_t ip6oj_type; - u_int8_t ip6oj_len; - u_int8_t ip6oj_jumbo_len[4]; -} __packed; - -/* NSAP Address Option */ -struct ip6_opt_nsap { - u_int8_t ip6on_type; - u_int8_t ip6on_len; - u_int8_t ip6on_src_nsap_len; - u_int8_t ip6on_dst_nsap_len; - /* followed by source NSAP */ - /* followed by destination NSAP */ -} __packed; - -/* Tunnel Limit Option */ -struct ip6_opt_tunnel { - u_int8_t ip6ot_type; - u_int8_t ip6ot_len; - u_int8_t ip6ot_encap_limit; -} __packed; - -/* Router Alert Option */ -struct ip6_opt_router { - u_int8_t ip6or_type; - u_int8_t ip6or_len; - u_int8_t ip6or_value[2]; -} __packed; #endif /* INET6 */ #define PFFRAG_SEENLAST 0x0001 /* Seen the last fragment for this */ diff --git a/sys/netinet/icmp6.h b/sys/netinet/icmp6.h index e1dc9116f8..e9ded24823 100644 --- a/sys/netinet/icmp6.h +++ b/sys/netinet/icmp6.h @@ -1,5 +1,5 @@ /* $FreeBSD: src/sys/netinet/icmp6.h,v 1.2.2.5 2002/06/29 18:31:11 ume Exp $ */ -/* $DragonFly: src/sys/netinet/icmp6.h,v 1.7 2006/05/20 02:42:12 dillon Exp $ */ +/* $DragonFly: src/sys/netinet/icmp6.h,v 1.8 2008/09/04 09:08:22 hasso Exp $ */ /* $KAME: icmp6.h,v 1.46 2001/04/27 15:09:48 itojun Exp $ */ /* @@ -115,6 +115,7 @@ struct icmp6_hdr { #define MLD_LISTENER_REPORT 131 /* multicast listener report */ #define ICMP6_MEMBERSHIP_REDUCTION 132 /* group membership termination */ #define MLD_LISTENER_DONE 132 /* multicast listener done */ +#define MLD_LISTENER_REDUCTION MLD_LISTENER_DONE /* defined in RFC3542 */ #ifndef _KERNEL /* the followings are for backward compatibility to old KAME apps. */ diff --git a/sys/netinet/in_pcb.h b/sys/netinet/in_pcb.h index 5c9528bf0b..b8a75acb64 100644 --- a/sys/netinet/in_pcb.h +++ b/sys/netinet/in_pcb.h @@ -65,7 +65,7 @@ * * @(#)in_pcb.h 8.1 (Berkeley) 6/10/93 * $FreeBSD: src/sys/netinet/in_pcb.h,v 1.32.2.7 2003/01/24 05:11:34 sam Exp $ - * $DragonFly: src/sys/netinet/in_pcb.h,v 1.23 2007/04/04 06:13:26 dillon Exp $ + * $DragonFly: src/sys/netinet/in_pcb.h,v 1.24 2008/09/04 09:08:22 hasso Exp $ */ #ifndef _NETINET_IN_PCB_H_ @@ -327,16 +327,24 @@ struct inpcbinfo { /* XXX documentation, prefixes */ #define IN6P_RTHDR 0x100000 /* receive routing header */ #define IN6P_RTHDRDSTOPTS 0x200000 /* receive dstoptions before rthdr */ #define IN6P_AUTOFLOWLABEL 0x800000 /* attach flowlabel automatically */ +/* + * RFC3542 Definition + */ +#define IN6P_TCLASS 0x400000 /* receive traffic class value */ +#define IN6P_RFC2292 0x40000000 /* used RFC2292 API on the socket */ +#define IN6P_MTU 0x80000000 /* receive path MTU */ #define INP_RECVTTL 0x80000000 /* receive incoming IP TTL */ #define INP_CONTROLOPTS (INP_RECVOPTS|INP_RECVRETOPTS|INP_RECVDSTADDR|\ - INP_RECVIF|INP_RECVTTL|\ + 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 f0f1857c70..5945737361 100644 --- a/sys/netinet/ip6.h +++ b/sys/netinet/ip6.h @@ -1,5 +1,5 @@ /* $FreeBSD: src/sys/netinet/ip6.h,v 1.2.2.2 2001/07/03 11:01:46 ume Exp $ */ -/* $DragonFly: src/sys/netinet/ip6.h,v 1.7 2007/05/23 08:57:09 dillon Exp $ */ +/* $DragonFly: src/sys/netinet/ip6.h,v 1.8 2008/09/04 09:08:22 hasso Exp $ */ /* $KAME: ip6.h,v 1.18 2001/03/29 05:34:30 itojun Exp $ */ /* @@ -157,6 +157,7 @@ struct ip6_dest { #define IP6OPT_TUNNEL_LIMIT 0x04 /* 00 0 00100 */ #define IP6OPT_RTALERT 0x05 /* 00 0 00101 (KAME definition) */ +#define IP6OPT_ROUTER_ALERT 0x05 /* 00 0 00101 (RFC3542, recommended) */ #define IP6OPT_RTALERT_LEN 4 #define IP6OPT_RTALERT_MLD 0 /* Datagram contains an MLD message */ #define IP6OPT_RTALERT_RSVP 1 /* Datagram contains an RSVP message */ @@ -177,8 +178,58 @@ struct ip6_dest { #define IP6OPT_MUTABLE 0x20 +/* Jumbo Payload Option */ +struct ip6_opt_jumbo { + u_int8_t ip6oj_type; + u_int8_t ip6oj_len; + u_int8_t ip6oj_jumbo_len[4]; +} __attribute__((__packed__)); #define IP6OPT_JUMBO_LEN 6 +/* IPv6 options: common part */ +struct ip6_opt { + u_int8_t ip6o_type; + u_int8_t ip6o_len; +} __attribute__((__packed__)); + + +/* NSAP Address Option */ +struct ip6_opt_nsap { + u_int8_t ip6on_type; + u_int8_t ip6on_len; + u_int8_t ip6on_src_nsap_len; + u_int8_t ip6on_dst_nsap_len; + /* followed by source NSAP */ + /* followed by destination NSAP */ +} __attribute__((__packed__)); + +/* Tunnel Limit Option */ +struct ip6_opt_tunnel { + u_int8_t ip6ot_type; + u_int8_t ip6ot_len; + u_int8_t ip6ot_encap_limit; +} __attribute__((__packed__)); + +/* Router Alert Option */ +struct ip6_opt_router { + u_int8_t ip6or_type; + u_int8_t ip6or_len; + u_int8_t ip6or_value[2]; +} __attribute__((__packed__)); + +/* Router alert values (in network byte order) */ +#if BYTE_ORDER == BIG_ENDIAN +#define IP6_ALERT_MLD 0x0000 +#define IP6_ALERT_RSVP 0x0001 +#define IP6_ALERT_AN 0x0002 +#else +#if BYTE_ORDER == LITTLE_ENDIAN +#define IP6_ALERT_MLD 0x0000 +#define IP6_ALERT_RSVP 0x0100 +#define IP6_ALERT_AN 0x0200 +#endif /* LITTLE_ENDIAN */ +#endif + /* Routing header */ struct ip6_rthdr { u_int8_t ip6r_nxt; /* next header */ @@ -188,17 +239,6 @@ struct ip6_rthdr { /* followed by routing type specific data */ } __attribute__((__packed__)); -/* Type 0 Routing header */ -struct ip6_rthdr0 { - u_int8_t ip6r0_nxt; /* next header */ - u_int8_t ip6r0_len; /* length in units of 8 octets */ - u_int8_t ip6r0_type; /* always zero */ - u_int8_t ip6r0_segleft; /* segments left */ - 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 */ -} __attribute__((__packed__)); - /* Fragment header */ struct ip6_frag { u_int8_t ip6f_nxt; /* next header */ diff --git a/sys/netinet6/icmp6.c b/sys/netinet6/icmp6.c index 3a8b47bee7..b94d0de03c 100644 --- a/sys/netinet6/icmp6.c +++ b/sys/netinet6/icmp6.c @@ -1,5 +1,5 @@ /* $FreeBSD: src/sys/netinet6/icmp6.c,v 1.6.2.13 2003/05/06 06:46:58 suz Exp $ */ -/* $DragonFly: src/sys/netinet6/icmp6.c,v 1.29 2008/06/21 12:30:19 aggelos Exp $ */ +/* $DragonFly: src/sys/netinet6/icmp6.c,v 1.30 2008/09/04 09:08:22 hasso Exp $ */ /* $KAME: icmp6.c,v 1.211 2001/04/04 05:56:20 itojun Exp $ */ /* @@ -889,7 +889,6 @@ icmp6_notify_error(struct mbuf *m, int off, int icmp6len, int code) int icmp6type = icmp6->icmp6_type; struct ip6_frag *fh; struct ip6_rthdr *rth; - struct ip6_rthdr0 *rth0; int rthlen; while (1) { /* XXX: should avoid infinite loop explicitly? */ @@ -943,36 +942,6 @@ icmp6_notify_error(struct mbuf *m, int off, int icmp6len, int code) } #endif rthlen = (rth->ip6r_len + 1) << 3; - /* - * XXX: currently there is no - * officially defined type other - * than type-0. - * Note that if the segment left field - * is 0, all intermediate hops must - * have been passed. - */ - if (rth->ip6r_segleft && - rth->ip6r_type == IPV6_RTHDR_TYPE_0) { - int hops; - -#ifndef PULLDOWN_TEST - IP6_EXTHDR_CHECK(m, 0, eoff + rthlen, - -1); - rth0 = (struct ip6_rthdr0 *)(mtod(m, caddr_t) + eoff); -#else - IP6_EXTHDR_GET(rth0, - struct ip6_rthdr0 *, m, - eoff, rthlen); - if (rth0 == NULL) { - icmp6stat.icp6s_tooshort++; - return (-1); - } -#endif - /* just ignore a bogus header */ - if ((rth0->ip6r0_len % 2) == 0 && - (hops = rth0->ip6r0_len/2)) - finaldst = (struct in6_addr *)(rth0 + 1) + (hops - 1); - } eoff += rthlen; nxt = rth->ip6r_nxt; break; diff --git a/sys/netinet6/in6.h b/sys/netinet6/in6.h index 427b9c1fee..2394c5bb93 100644 --- a/sys/netinet6/in6.h +++ b/sys/netinet6/in6.h @@ -1,5 +1,5 @@ /* $FreeBSD: src/sys/netinet6/in6.h,v 1.7.2.7 2002/08/01 19:38:50 ume Exp $ */ -/* $DragonFly: src/sys/netinet6/in6.h,v 1.10 2007/08/21 20:03:20 corecode Exp $ */ +/* $DragonFly: src/sys/netinet6/in6.h,v 1.11 2008/09/04 09:08:22 hasso Exp $ */ /* $KAME: in6.h,v 1.89 2001/05/27 13:28:35 itojun Exp $ */ /* @@ -395,13 +395,15 @@ struct route_in6 { #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; only bind INET6 at wildcard bind */ @@ -422,11 +424,58 @@ struct route_in6 { #define IPV6_FW_GET 34 /* get entire firewall rule chain */ #endif +/* + * new socket options introduced in RFC3542 + */ +#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 */ + +/* mtuinfo; get the current path MTU (sopt), + * 4 bytes int; MTU notification (cmsg) + */ +#define IPV6_PATHMTU 44 +#if 0 /*obsoleted during 2292bis -> 3542*/ +#define IPV6_REACHCONF 45 /* no data; ND reachability confirm + (cmsg only/not in of RFC3542) */ +#endif +#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 */ +#define IPV6_PKTOPTIONS 52 /* buf/cmsghdr; set/get IPv6 options, this is obsoleted by RFC3542 */ + +#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. + */ +/* + * The following option is private; do not use it from user applications. + * It is deliberately defined to the same value as IP_MSFILTER. + */ +#define IPV6_MSFILTER 74 /* struct __msfilterreq; + * set/get multicast source filter list. + */ /* 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 */ #define IPV6_RTHDR_STRICT 1 /* this hop must be a neighbor. XXX old spec */ -#define IPV6_RTHDR_TYPE_0 0 /* IPv6 routing header type 0 */ /* * Defaults and limits for options @@ -450,6 +499,14 @@ struct in6_pktinfo { unsigned int ipi6_ifindex; /* send/recv interface index */ }; +/* + * New Control structure for IPV6_RECVPATHMTU socket option introduced in RFC3542. + */ +struct ip6_mtuinfo { + struct sockaddr_in6 ip6m_addr; /* or sockaddr_storage? */ + uint32_t ip6m_mtu; +}; + /* * Argument for IPV6_PORTRANGE: * - which range to search when port is unspecified at bind() or connect() diff --git a/sys/netinet6/in6_pcb.c b/sys/netinet6/in6_pcb.c index 65a5a06fda..3fa30a66b7 100644 --- a/sys/netinet6/in6_pcb.c +++ b/sys/netinet6/in6_pcb.c @@ -1,5 +1,5 @@ /* $FreeBSD: src/sys/netinet6/in6_pcb.c,v 1.10.2.9 2003/01/24 05:11:35 sam Exp $ */ -/* $DragonFly: src/sys/netinet6/in6_pcb.c,v 1.34 2008/01/05 14:02:40 swildner Exp $ */ +/* $DragonFly: src/sys/netinet6/in6_pcb.c,v 1.35 2008/09/04 09:08:22 hasso Exp $ */ /* $KAME: in6_pcb.c,v 1.31 2001/05/21 05:45:10 jinmei Exp $ */ /* @@ -879,6 +879,20 @@ in6_pcbnotify(struct inpcbhead *head, struct sockaddr *dst, in_port_t fport, if ((inp->inp_vflag & INP_IPV6) == 0) continue; + /* + * If the error designates a new path MTU for a destination + * and the application (associated with this socket) wanted to + * know the value, notify. Note that we notify for all + * disconnected sockets if the corresponding application + * wanted. This is because some UDP applications keep sending + * sockets disconnected. + * XXX: should we avoid to notify the value to TCP sockets? + */ + if (cmd == PRC_MSGSIZE && (inp->inp_flags & IN6P_MTU) != 0 && + (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr) || + IN6_ARE_ADDR_EQUAL(&inp->in6p_faddr, &sa6_dst->sin6_addr))) { + ip6_notify_pmtu(inp, (struct sockaddr_in6 *)dst, arg); + } /* * Detect if we should notify the error. If no source and diff --git a/sys/netinet6/ip6_input.c b/sys/netinet6/ip6_input.c index fc12beb77f..a89dc38d06 100644 --- a/sys/netinet6/ip6_input.c +++ b/sys/netinet6/ip6_input.c @@ -1,5 +1,5 @@ /* $FreeBSD: src/sys/netinet6/ip6_input.c,v 1.11.2.15 2003/01/24 05:11:35 sam Exp $ */ -/* $DragonFly: src/sys/netinet6/ip6_input.c,v 1.35 2007/10/14 18:15:19 hasso Exp $ */ +/* $DragonFly: src/sys/netinet6/ip6_input.c,v 1.36 2008/09/04 09:08:22 hasso Exp $ */ /* $KAME: ip6_input.c,v 1.259 2002/01/21 04:58:09 jinmei Exp $ */ /* @@ -1123,6 +1123,7 @@ void ip6_savecontrol(struct inpcb *in6p, struct mbuf **mp, struct ip6_hdr *ip6, struct mbuf *m) { + #define IS2292(x, y) ((in6p->in6p_flags & IN6P_RFC2292) ? (x) : (y)) struct thread *td = curthread; /* XXX */ int privileged = 0; int rthdr_exist = 0; @@ -1154,7 +1155,7 @@ ip6_savecontrol(struct inpcb *in6p, struct mbuf **mp, struct ip6_hdr *ip6, ? m->m_pkthdr.rcvif->if_index : 0; *mp = sbcreatecontrol((caddr_t) &pi6, - sizeof(struct in6_pktinfo), IPV6_PKTINFO, + sizeof(struct in6_pktinfo), IS2292(IPV6_2292PKTINFO, IPV6_PKTINFO), IPPROTO_IPV6); if (*mp) mp = &(*mp)->m_next; @@ -1163,7 +1164,21 @@ ip6_savecontrol(struct inpcb *in6p, struct mbuf **mp, struct ip6_hdr *ip6, if (in6p->in6p_flags & IN6P_HOPLIMIT) { int hlim = ip6->ip6_hlim & 0xff; *mp = sbcreatecontrol((caddr_t) &hlim, - sizeof(int), IPV6_HOPLIMIT, IPPROTO_IPV6); + sizeof(int), 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; } @@ -1216,7 +1231,7 @@ ip6_savecontrol(struct inpcb *in6p, struct mbuf **mp, struct ip6_hdr *ip6, * Note: this constraint is removed in 2292bis. */ *mp = sbcreatecontrol((caddr_t)hbh, hbhlen, - IPV6_HOPOPTS, IPPROTO_IPV6); + IS2292(IPV6_2292HOPOPTS, IPV6_HOPOPTS), IPPROTO_IPV6); if (*mp) mp = &(*mp)->m_next; #ifdef PULLDOWN_TEST @@ -1331,7 +1346,7 @@ ip6_savecontrol(struct inpcb *in6p, struct mbuf **mp, struct ip6_hdr *ip6, break; *mp = sbcreatecontrol((caddr_t)ip6e, elen, - IPV6_DSTOPTS, + IS2292(IPV6_2292DSTOPTS, IPV6_DSTOPTS), IPPROTO_IPV6); if (*mp) mp = &(*mp)->m_next; @@ -1341,7 +1356,7 @@ ip6_savecontrol(struct inpcb *in6p, struct mbuf **mp, struct ip6_hdr *ip6, break; *mp = sbcreatecontrol((caddr_t)ip6e, elen, - IPV6_RTHDR, + IS2292(IPV6_2292RTHDR, IPV6_RTHDR), IPPROTO_IPV6); if (*mp) mp = &(*mp)->m_next; @@ -1376,7 +1391,42 @@ ip6_savecontrol(struct inpcb *in6p, struct mbuf **mp, struct ip6_hdr *ip6, loopend: ; } +#undef IS2292 +} + +void +ip6_notify_pmtu(struct inpcb *in6p, struct sockaddr_in6 *dst, u_int32_t *mtu) +{ + struct socket *so; + struct mbuf *m_mtu; + struct ip6_mtuinfo mtuctl; + + so = in6p->inp_socket; + + if (mtu == NULL) + return; + +#ifdef DIAGNOSTIC + if (so == NULL) /* I believe this is impossible */ + panic("ip6_notify_pmtu: socket is NULL"); +#endif + + bzero(&mtuctl, sizeof(mtuctl)); /* zero-clear for safety */ + mtuctl.ip6m_mtu = *mtu; + mtuctl.ip6m_addr = *dst; + + if ((m_mtu = sbcreatecontrol((caddr_t)&mtuctl, sizeof(mtuctl), + IPV6_PATHMTU, IPPROTO_IPV6)) == NULL) + return; + + if (sbappendaddr(&so->so_rcv, (struct sockaddr *)dst, NULL, m_mtu) + == 0) { + m_freem(m_mtu); + /* XXX: should count statistics */ + } else + sorwakeup(so); + return; } #ifdef PULLDOWN_TEST diff --git a/sys/netinet6/ip6_output.c b/sys/netinet6/ip6_output.c index c8e54ef15a..111e5c311b 100644 --- a/sys/netinet6/ip6_output.c +++ b/sys/netinet6/ip6_output.c @@ -1,5 +1,5 @@ /* $FreeBSD: src/sys/netinet6/ip6_output.c,v 1.13.2.18 2003/01/24 05:11:35 sam Exp $ */ -/* $DragonFly: src/sys/netinet6/ip6_output.c,v 1.36 2008/06/18 11:38:37 swildner Exp $ */ +/* $DragonFly: src/sys/netinet6/ip6_output.c,v 1.37 2008/09/04 09:08:22 hasso Exp $ */ /* $KAME: ip6_output.c,v 1.279 2002/01/26 06:12:30 jinmei Exp $ */ /* @@ -94,6 +94,7 @@ #include #include #include +#include #ifdef IPSEC #include @@ -123,15 +124,22 @@ struct ip6_exthdrs { struct mbuf *ip6e_dest2; }; +static int ip6_pcbopt (int, u_char *, int, struct ip6_pktopts **, int); +static int ip6_setpktoption (int, u_char *, int, struct ip6_pktopts *, + int, int, int, int); static int ip6_pcbopts (struct ip6_pktopts **, struct mbuf *, - struct socket *, struct sockopt *sopt); + struct socket *, struct sockopt *); +static int ip6_getpcbopt(struct ip6_pktopts *, int, struct sockopt *); static int ip6_setmoptions (int, struct ip6_moptions **, struct mbuf *); static int ip6_getmoptions (int, struct ip6_moptions *, struct mbuf **); +static int ip6_getpmtu(struct route_in6 *, struct route_in6 *, + struct ifnet *, struct in6_addr *, u_long *, int *); static int copyexthdr (void *, struct mbuf **); static int ip6_insertfraghdr (struct mbuf *, struct mbuf *, int, struct ip6_frag **); static int ip6_insert_jumboopt (struct ip6_exthdrs *, u_int32_t); static struct mbuf *ip6_splithdr (struct mbuf *); +static int copypktopts(struct ip6_pktopts *, struct ip6_pktopts *, int); /* * IP6 output. The packet in mbuf chain m contains a skeletal IP6 @@ -161,6 +169,7 @@ ip6_output(struct mbuf *m0, struct ip6_pktopts *opt, struct route_in6 *ro, int error = 0; struct in6_ifaddr *ia = NULL; u_long mtu; + int alwaysfrag, dontfrag; u_int32_t optlen, plen = 0, unfragpartlen; struct ip6_exthdrs exthdrs; struct in6_addr finaldst; @@ -447,18 +456,10 @@ ip6_output(struct mbuf *m0, struct ip6_pktopts *opt, struct route_in6 *ro, */ if (exthdrs.ip6e_rthdr) { struct ip6_rthdr *rh; - struct ip6_rthdr0 *rh0; finaldst = ip6->ip6_dst; rh = mtod(exthdrs.ip6e_rthdr, struct ip6_rthdr *); switch (rh->ip6r_type) { - case IPV6_RTHDR_TYPE_0: - rh0 = (struct ip6_rthdr0 *)rh; - ip6->ip6_dst = rh0->ip6r0_addr[0]; - bcopy(&rh0->ip6r0_addr[1], &rh0->ip6r0_addr[0], - sizeof(struct in6_addr)*(rh0->ip6r0_segleft - 1)); - rh0->ip6r0_addr[rh0->ip6r0_segleft - 1] = finaldst; - break; default: /* is it possible? */ error = EINVAL; goto bad; @@ -745,59 +746,35 @@ ip6_output(struct mbuf *m0, struct ip6_pktopts *opt, struct route_in6 *ro, if (ifpp) *ifpp = ifp; + /* Determine path MTU. */ + if ((error = ip6_getpmtu(ro_pmtu, ro, ifp, &finaldst, &mtu, + &alwaysfrag)) != 0) + goto bad; + /* - * Determine path MTU. + * The caller of this function may specify to use the minimum MTU + * in some cases. + * An advanced API option (IPV6_USE_MIN_MTU) can also override MTU + * setting. The logic is a bit complicated; by default, unicast + * packets will follow path MTU while multicast packets will be sent at + * the minimum MTU. If IP6PO_MINMTU_ALL is specified, all packets + * including unicast ones will be sent at the minimum MTU. Multicast + * packets will always be sent at the minimum MTU unless + * IP6PO_MINMTU_DISABLE is explicitly specified. + * See RFC 3542 for more details. */ - if (ro_pmtu != ro) { - /* The first hop and the final destination may differ. */ - struct sockaddr_in6 *sin6_fin = - (struct sockaddr_in6 *)&ro_pmtu->ro_dst; - - if (ro_pmtu->ro_rt != NULL && - (!(ro->ro_rt->rt_flags & RTF_UP) || - !IN6_ARE_ADDR_EQUAL(&sin6_fin->sin6_addr, &finaldst))) { - RTFREE(ro_pmtu->ro_rt); - ro_pmtu->ro_rt = NULL; - } - if (ro_pmtu->ro_rt == NULL) { - bzero(sin6_fin, sizeof(*sin6_fin)); - sin6_fin->sin6_family = AF_INET6; - sin6_fin->sin6_len = sizeof(struct sockaddr_in6); - sin6_fin->sin6_addr = finaldst; - - rtalloc((struct route *)ro_pmtu); - } - } - if (ro_pmtu->ro_rt != NULL) { - u_int32_t ifmtu = ND_IFINFO(ifp)->linkmtu; - - mtu = ro_pmtu->ro_rt->rt_rmx.rmx_mtu; - if (mtu > ifmtu || mtu == 0) { - /* - * The MTU on the route is larger than the MTU on - * the interface! This shouldn't happen, unless the - * MTU of the interface has been changed after the - * interface was brought up. Change the MTU in the - * route to match the interface MTU (as long as the - * field isn't locked). - * - * if MTU on the route is 0, we need to fix the MTU. - * this case happens with path MTU discovery timeouts. - */ - mtu = ifmtu; - if (!(ro_pmtu->ro_rt->rt_rmx.rmx_locks & RTV_MTU)) - ro_pmtu->ro_rt->rt_rmx.rmx_mtu = mtu; /* XXX */ + if (mtu > IPV6_MMTU) { + if ((flags & IPV6_MINMTU)) + mtu = IPV6_MMTU; + else if (opt && opt->ip6po_minmtu == IP6PO_MINMTU_ALL) + mtu = IPV6_MMTU; + else if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) && + (opt == NULL || + opt->ip6po_minmtu != IP6PO_MINMTU_DISABLE)) { + mtu = IPV6_MMTU; } - } else { - mtu = ND_IFINFO(ifp)->linkmtu; } - /* - * advanced API (IPV6_USE_MIN_MTU) overrides mtu setting - */ - if ((flags & IPV6_MINMTU) != 0 && mtu > IPV6_MMTU) - mtu = IPV6_MMTU; - /* Fake scoped addresses */ if ((ifp->if_flags & IFF_LOOPBACK) != 0) { /* @@ -909,36 +886,80 @@ ip6_output(struct mbuf *m0, struct ip6_pktopts *opt, struct route_in6 *ro, /* * 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; + kpfctlinput2(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) { + } + + /* + * try to fragment the packet. case 1-b and 3 + */ + if (mtu < IPV6_MMTU) { /* * note that path MTU is never less than IPV6_MMTU * (see icmp6_input). @@ -954,6 +975,7 @@ ip6_output(struct mbuf *m0, struct ip6_pktopts *opt, struct route_in6 *ro, struct mbuf **mnext, *m_frgpart; struct ip6_frag *ip6f; u_int32_t id = htonl(ip6_id++); + int qslots = ifp->if_snd.ifq_maxlen - ifp->if_snd.ifq_len; u_char nextproto; /* @@ -972,6 +994,17 @@ ip6_output(struct mbuf *m0, struct ip6_pktopts *opt, struct route_in6 *ro, goto bad; } + /* + * Verify that we have any chance at all of being able to queue + * the packet or packet fragments + */ + if (qslots <= 0 || ((u_int)qslots * (mtu - hlen) + < tlen /* - hlen */)) { + error = ENOBUFS; + ip6stat.ip6s_odropped++; + goto bad; + } + mnext = &m->m_nextpkt; /* @@ -1257,14 +1290,92 @@ ip6_insertfraghdr(struct mbuf *m0, struct mbuf *m, int hlen, return (0); } +static int +ip6_getpmtu(struct route_in6 *ro_pmtu, struct route_in6 *ro, + struct ifnet *ifp, struct in6_addr *dst, u_long *mtup, + int *alwaysfragp) +{ + u_int32_t mtu = 0; + int alwaysfrag = 0; + int error = 0; + + 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; + if (ro_pmtu->ro_rt && + ((ro_pmtu->ro_rt->rt_flags & RTF_UP) == 0 || + !IN6_ARE_ADDR_EQUAL(&sa6_dst->sin6_addr, dst))) { + RTFREE(ro_pmtu->ro_rt); + ro_pmtu->ro_rt = (struct rtentry *)NULL; + } + if (ro_pmtu->ro_rt == NULL) { + bzero(sa6_dst, sizeof(*sa6_dst)); + sa6_dst->sin6_family = AF_INET6; + sa6_dst->sin6_len = sizeof(struct sockaddr_in6); + sa6_dst->sin6_addr = *dst; + + rtalloc((struct route *)ro_pmtu); + } + } + if (ro_pmtu->ro_rt) { + u_int32_t ifmtu; + struct in_conninfo inc; + + bzero(&inc, sizeof(inc)); + inc.inc_flags = 1; /* IPv6 */ + inc.inc6_faddr = *dst; + + if (ifp == NULL) + ifp = ro_pmtu->ro_rt->rt_ifp; + ifmtu = IN6_LINKMTU(ifp); + mtu = ro_pmtu->ro_rt->rt_rmx.rmx_mtu; + if (mtu == 0) + mtu = ifmtu; + 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 + * MTU of the interface has been changed after the + * interface was brought up. Change the MTU in the + * route to match the interface MTU (as long as the + * field isn't locked). + */ + mtu = ifmtu; + ro_pmtu->ro_rt->rt_rmx.rmx_mtu = mtu; + } + } else if (ifp) { + mtu = IN6_LINKMTU(ifp); + } else + error = EHOSTUNREACH; /* XXX */ + + *mtup = mtu; + if (alwaysfragp) + *alwaysfragp = alwaysfrag; + return (error); +} + /* * IP6 socket option processing. */ int ip6_ctloutput(struct socket *so, struct sockopt *sopt) { + int optdatalen,uproto; int privileged; struct inpcb *in6p = so->so_pcb; + void *optdata; int error, optval; int level, op, optname; int optlen; @@ -1283,6 +1394,7 @@ ip6_ctloutput(struct socket *so, struct sockopt *sopt) } error = optval = 0; + uproto = (int)so->so_proto->pr_protocol; privileged = (td == NULL || suser(td)) ? 0 : 1; if (level == IPPROTO_IPV6) { @@ -1290,7 +1402,10 @@ ip6_ctloutput(struct socket *so, struct sockopt *sopt) case SOPT_SET: switch (optname) { + case IPV6_2292PKTOPTIONS: +#ifdef IPV6_PKTOPTIONS case IPV6_PKTOPTIONS: +#endif { struct mbuf *m; @@ -1317,8 +1432,20 @@ ip6_ctloutput(struct socket *so, struct sockopt *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) + return (EPERM); + case IPV6_RECVPKTINFO: + case IPV6_RECVHOPLIMIT: + case IPV6_RECVRTHDR: + case IPV6_RECVPATHMTU: + case IPV6_RECVTCLASS: + case IPV6_AUTOFLOWLABEL: + case IPV6_HOPLIMIT: + /* FALLTHROUGH */ case IPV6_UNICAST_HOPS: - case IPV6_CHECKSUM: case IPV6_FAITH: case IPV6_V6ONLY: @@ -1352,9 +1479,110 @@ do { \ in6p->in6p_flags &= ~(bit); \ } while (0) #define OPTBIT(bit) (in6p->in6p_flags & (bit) ? 1 : 0) +/* + * Although changed to RFC3542, It's better to also support RFC2292 API + */ +#define OPTSET2292(bit) \ +do { \ + in6p->in6p_flags |= IN6P_RFC2292; \ + if (optval) \ + in6p->in6p_flags |= (bit); \ + else \ + in6p->in6p_flags &= ~(bit); \ +} while (/*CONSTCOND*/ 0) + + 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, 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_CHECKSUM: - in6p->in6p_cksum = optval; + 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_RECVPATHMTU: + /* + * We ignore this option for TCP + * sockets. + * (RFC3542 leaves this case + * unspecified.) + */ + if (uproto != IPPROTO_TCP) + OPTSET(IN6P_MTU); + 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; case IPV6_FAITH: @@ -1365,7 +1593,6 @@ do { \ /* * make setsockopt(IPV6_V6ONLY) * available only prior to bind(2). - * see ipng mailing list, Jun 22 2001. */ if (in6p->in6p_lport || !IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr)) @@ -1382,11 +1609,32 @@ do { \ } 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 = soopt_to_kbuf(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, 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; @@ -1397,31 +1645,69 @@ 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 (RFC3542) + */ + u_char *optbuf; + u_char optbuf_storage[MCLBYTES]; + int optlen; + struct ip6_pktopts **optp; + + /* cannot mix with RFC2292 */ + if (OPTBIT(IN6P_RFC2292)) { + error = EINVAL; break; } + + /* + * We only ensure valsize is not too large + * here. Further validation will be done + * later. + */ + error = soopt_to_kbuf(sopt, optbuf_storage, + sizeof(optbuf_storage), 0); + if (error) + break; + optlen = sopt->sopt_valsize; + optbuf = optbuf_storage; + optp = &in6p->in6p_outputopts; + error = ip6_pcbopt(optname, optbuf, optlen, + optp, uproto); break; + } #undef OPTSET case IPV6_MULTICAST_IF: @@ -1528,8 +1814,19 @@ do { \ case SOPT_GET: switch (optname) { - + case IPV6_2292PKTOPTIONS: +#ifdef IPV6_PKTOPTIONS case IPV6_PKTOPTIONS: +#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. + */ if (in6p->in6p_options) { struct mbuf *m; m = m_copym(in6p->in6p_options, @@ -1540,21 +1837,61 @@ do { \ } else 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_RECVTCLASS: + case IPV6_AUTOFLOWLABEL: case IPV6_FAITH: case IPV6_V6ONLY: case IPV6_PORTRANGE: switch (optname) { - case IPV6_UNICAST_HOPS: - optval = in6p->in6p_hops; + 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_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_RECVTCLASS: + optval = OPTBIT(IN6P_TCLASS); + break; + + case IPV6_AUTOFLOWLABEL: + optval = OPTBIT(IN6P_AUTOFLOWLABEL); break; - case IPV6_CHECKSUM: - optval = in6p->in6p_cksum; + + case IPV6_UNICAST_HOPS: + optval = in6p->in6p_hops; break; case IPV6_FAITH: @@ -1579,41 +1916,88 @@ do { \ } } soopt_from_kbuf(sopt, &optval, - sizeof 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 || + case IPV6_PATHMTU: + { + u_long pmtu = 0; + struct ip6_mtuinfo mtuinfo; + struct route_in6 sro; + + bzero(&sro, sizeof(sro)); + + 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(&sro, NULL, NULL, + &in6p->in6p_faddr, &pmtu, NULL); + if (sro.ro_rt) + RTFREE(sro.ro_rt); + 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); + soopt_from_kbuf(sopt, optdata, + optdatalen); + break; + } + + case IPV6_2292PKTINFO: + case IPV6_2292HOPLIMIT: + case IPV6_2292HOPOPTS: + case IPV6_2292RTHDR: + case IPV6_2292DSTOPTS: + if (optname == IPV6_2292HOPOPTS || + optname == IPV6_2292DSTOPTS || !privileged) return (EPERM); 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: + case IPV6_2292HOPOPTS: if (!privileged) return (EPERM); optval = OPTBIT(IN6P_HOPOPTS); break; - case IPV6_RTHDR: + case IPV6_2292RTHDR: optval = OPTBIT(IN6P_RTHDR); break; - case IPV6_DSTOPTS: + case IPV6_2292DSTOPTS: if (!privileged) return (EPERM); optval = OPTBIT(IN6P_DSTOPTS|IN6P_RTHDRDSTOPTS); break; } soopt_from_kbuf(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: @@ -1627,7 +2011,7 @@ do { \ in6p->in6p_moptions, &m); if (error == 0) soopt_from_kbuf(sopt, - mtod(m, char *), m->m_len); + mtod(m, char *), m->m_len); m_freem(m); } break; @@ -1686,33 +2070,106 @@ do { \ return (error); } -/* - * Set up IP6 options in pcb for insertion in output packets or - * specifying behavior of outgoing packets. - */ -static int -ip6_pcbopts(struct ip6_pktopts **pktopt, struct mbuf *m, struct socket *so, - struct sockopt *sopt) +int +ip6_raw_ctloutput(struct socket *so, struct sockopt *sopt) { - struct ip6_pktopts *opt = *pktopt; - int error = 0; - struct thread *td = sopt->sopt_td; - int priv = 0; - - /* turn off any old options. */ - if (opt) { -#ifdef DIAGNOSTIC - if (opt->ip6po_pktinfo || opt->ip6po_nexthop || - opt->ip6po_hbh || opt->ip6po_dest1 || opt->ip6po_dest2 || - opt->ip6po_rhinfo.ip6po_rhi_rthdr) - kprintf("ip6_pcbopts: all specified options are cleared.\n"); -#endif - ip6_clearpktopts(opt, 1, -1); - } else - opt = kmalloc(sizeof(*opt), M_IP6OPT, M_WAITOK); - *pktopt = NULL; + int error = 0, optval, optlen; + const int icmp6off = offsetof(struct icmp6_hdr, icmp6_cksum); + struct in6pcb *in6p = sotoin6pcb(so); + int level, op, optname; - if (m == NULL || m->m_len == 0) { + if (sopt) { + level = sopt->sopt_level; + op = sopt->sopt_dir; + optname = sopt->sopt_name; + optlen = sopt->sopt_valsize; + } else + panic("ip6_raw_ctloutput: arg soopt is NULL"); + + if (level != IPPROTO_IPV6) { + return (EINVAL); + } + + switch (optname) { + case IPV6_CHECKSUM: + /* + * For ICMPv6 sockets, no modification allowed for checksum + * offset, permit "no change" values to help existing apps. + * + * RFC3542 says: "An attempt to set IPV6_CHECKSUM + * for an ICMPv6 socket will fail." + * The current behavior does not meet RFC3542. + */ + switch (op) { + case SOPT_SET: + if (optlen != sizeof(int)) { + error = EINVAL; + break; + } + error = soopt_to_kbuf(sopt, &optval, + sizeof optval, sizeof optval); + if (error) + break; + if ((optval % 2) != 0) { + /* the API assumes even offset values */ + error = EINVAL; + } else if (so->so_proto->pr_protocol == + IPPROTO_ICMPV6) { + if (optval != icmp6off) + error = EINVAL; + } else + in6p->in6p_cksum = optval; + break; + + case SOPT_GET: + if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) + optval = icmp6off; + else + optval = in6p->in6p_cksum; + + soopt_from_kbuf(sopt, &optval, sizeof(optval)); + break; + + default: + error = EINVAL; + break; + } + break; + + default: + error = ENOPROTOOPT; + break; + } + + return (error); +} + +/* + * Set up IP6 options in pcb for insertion in output packets or + * specifying behavior of outgoing packets. + */ +static int +ip6_pcbopts(struct ip6_pktopts **pktopt, struct mbuf *m, + struct socket *so, struct sockopt *sopt) +{ + int priv = 0; + struct ip6_pktopts *opt = *pktopt; + int error = 0; + + /* turn off any old options. */ + if (opt) { +#ifdef DIAGNOSTIC + if (opt->ip6po_pktinfo || opt->ip6po_nexthop || + opt->ip6po_hbh || opt->ip6po_dest1 || opt->ip6po_dest2 || + opt->ip6po_rhinfo.ip6po_rhi_rthdr) + kprintf("ip6_pcbopts: all specified options are cleared.\n"); +#endif + ip6_clearpktopts(opt, -1); + } else + opt = kmalloc(sizeof(*opt), M_IP6OPT, M_WAITOK); + *pktopt = NULL; + + if (!m || m->m_len == 0) { /* * Only turning off any previous options, regardless of * whether the opt is just created or given. @@ -1722,10 +2179,8 @@ ip6_pcbopts(struct ip6_pktopts **pktopt, struct mbuf *m, struct socket *so, } /* set options specified by user. */ - if (suser(td) == 0) - 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, so->so_proto->pr_protocol, priv)) != 0) { + ip6_clearpktopts(opt, -1); /* XXX: discard all options */ kfree(opt, M_IP6OPT); return (error); } @@ -1733,6 +2188,128 @@ ip6_pcbopts(struct ip6_pktopts **pktopt, struct mbuf *m, struct socket *so, return (0); } + +/* + * Below three functions are introduced by merge to RFC3542 + */ + +static int +ip6_getpcbopt(struct ip6_pktopts *pktopt, int optname, struct sockopt *sopt) +{ + 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); + } + + soopt_from_kbuf(sopt, optdata, optdatalen); + + return (error); +} + +/* + * initialize ip6_pktopts. beware that there are non-zero default values in + * the struct. + */ + +static int +ip6_pcbopt(int optname, u_char *buf, int len, struct ip6_pktopts **pktopt, int uproto) +{ + struct ip6_pktopts *opt; + int priv =0; + if (*pktopt == NULL) { + *pktopt = kmalloc(sizeof(*opt), M_IP6OPT, M_WAITOK); + init_ip6pktopts(*pktopt); + } + opt = *pktopt; + + return (ip6_setpktoption(optname, buf, len, opt, 1, 0, uproto, priv)); +} + /* * initialize ip6_pktopts. beware that there are non-zero default values in * the struct. @@ -1743,38 +2320,47 @@ init_ip6pktopts(struct ip6_pktopts *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; } void -ip6_clearpktopts(struct ip6_pktopts *pktopt, int needfree, int optname) +ip6_clearpktopts(struct ip6_pktopts *pktopt, int optname) { if (pktopt == NULL) return; - if (optname == -1) { - if (needfree && pktopt->ip6po_pktinfo) + if (optname == -1 || optname == IPV6_PKTINFO) { + if (pktopt->ip6po_pktinfo) kfree(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 (needfree && pktopt->ip6po_nexthop) + 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 (pktopt->ip6po_nexthop) kfree(pktopt->ip6po_nexthop, M_IP6OPT); pktopt->ip6po_nexthop = NULL; } - if (optname == -1) { - if (needfree && pktopt->ip6po_hbh) + if (optname == -1 || optname == IPV6_HOPOPTS) { + if (pktopt->ip6po_hbh) kfree(pktopt->ip6po_hbh, M_IP6OPT); pktopt->ip6po_hbh = NULL; } - if (optname == -1) { - if (needfree && pktopt->ip6po_dest1) + if (optname == -1 || optname == IPV6_RTHDRDSTOPTS) { + if (pktopt->ip6po_dest1) kfree(pktopt->ip6po_dest1, M_IP6OPT); pktopt->ip6po_dest1 = NULL; } - if (optname == -1) { - if (needfree && pktopt->ip6po_rhinfo.ip6po_rhi_rthdr) + if (optname == -1 || optname == IPV6_RTHDR) { + if (pktopt->ip6po_rhinfo.ip6po_rhi_rthdr) kfree(pktopt->ip6po_rhinfo.ip6po_rhi_rthdr, M_IP6OPT); pktopt->ip6po_rhinfo.ip6po_rhi_rthdr = NULL; if (pktopt->ip6po_route.ro_rt) { @@ -1782,8 +2368,8 @@ ip6_clearpktopts(struct ip6_pktopts *pktopt, int needfree, int optname) pktopt->ip6po_route.ro_rt = NULL; } } - if (optname == -1) { - if (needfree && pktopt->ip6po_dest2) + if (optname == -1 || optname == IPV6_DSTOPTS) { + if (pktopt->ip6po_dest2) kfree(pktopt->ip6po_dest2, M_IP6OPT); pktopt->ip6po_dest2 = NULL; } @@ -1847,6 +2433,45 @@ bad: kfree(dst, M_IP6OPT); return (NULL); } + +static int +copypktopts(struct ip6_pktopts *dst, struct ip6_pktopts *src, int canwait) +{ + if (dst == NULL || src == NULL) { +#ifdef DIAGNOSTIC + kprintf("ip6_clearpktopts: invalid argument\n"); +#endif + return (EINVAL); + } + + 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 = kmalloc(sizeof(*dst->ip6po_pktinfo), + M_IP6OPT, canwait); + if (dst->ip6po_pktinfo == NULL) + goto bad; + *dst->ip6po_pktinfo = *src->ip6po_pktinfo; + } + if (src->ip6po_nexthop) { + dst->ip6po_nexthop = kmalloc(src->ip6po_nexthop->sa_len, + M_IP6OPT, canwait); + if (dst->ip6po_nexthop == NULL) + goto bad; + bcopy(src->ip6po_nexthop, dst->ip6po_nexthop, + src->ip6po_nexthop->sa_len); + } + PKTOPT_EXTHDRCPY(ip6po_hbh); + PKTOPT_EXTHDRCPY(ip6po_dest1); + PKTOPT_EXTHDRCPY(ip6po_dest2); + PKTOPT_EXTHDRCPY(ip6po_rthdr); /* not copy the cached route */ + return (0); + + bad: + ip6_clearpktopts(dst, -1); + return (ENOBUFS); +} #undef PKTOPT_EXTHDRCPY void @@ -1855,7 +2480,7 @@ ip6_freepcbopts(struct ip6_pktopts *pktopt) if (pktopt == NULL) return; - ip6_clearpktopts(pktopt, 1, -1); + ip6_clearpktopts(pktopt, -1); kfree(pktopt, M_IP6OPT); } @@ -2205,213 +2830,448 @@ ip6_freemoptions(struct ip6_moptions *im6o) } /* - * Set IPv6 outgoing packet options based on advanced API. + * 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 RFC3542 ancillary data + * "sticky=1, cmsg=0": RFC3542 socket option + * "sticky=1, cmsg=1": RFC2292 socket option */ -int -ip6_setpktoptions(struct mbuf *control, struct ip6_pktopts *opt, int priv, - int needcopy) +static int +ip6_setpktoption(int optname, u_char *buf, int len, struct ip6_pktopts *opt, + int sticky, int cmsg, int uproto, int priv) { - struct cmsghdr *cm = NULL; + int minmtupolicy, preftemp; + //int error; - if (control == NULL || opt == NULL) + if (!sticky && !cmsg) { + kprintf("ip6_setpktoption: impossible case\n"); return (EINVAL); - - init_ip6pktopts(opt); + } /* - * XXX: Currently, we assume all the optional information is stored - * in a single mbuf. + * IPV6_2292xxx is for backward compatibility to RFC2292, and should + * not be specified in the context of RFC3542. Conversely, + * RFC3542 types should not be specified in the context of RFC2292. */ - if (control->m_next) - return (EINVAL); + if (!cmsg) { + switch (optname) { + case IPV6_2292PKTINFO: + case IPV6_2292HOPLIMIT: + case IPV6_2292NEXTHOP: + case IPV6_2292HOPOPTS: + case IPV6_2292DSTOPTS: + case IPV6_2292RTHDR: + case IPV6_2292PKTOPTIONS: + return (ENOPROTOOPT); + } + } + if (sticky && cmsg) { + switch (optname) { + case IPV6_PKTINFO: + case IPV6_HOPLIMIT: + case IPV6_NEXTHOP: + case IPV6_HOPOPTS: + case IPV6_DSTOPTS: + case IPV6_RTHDRDSTOPTS: + case IPV6_RTHDR: + case IPV6_USE_MIN_MTU: + case IPV6_DONTFRAG: + case IPV6_TCLASS: + case IPV6_PREFER_TEMPADDR: /* XXX: not an RFC3542 option */ + return (ENOPROTOOPT); + } + } - for (; control->m_len; control->m_data += CMSG_ALIGN(cm->cmsg_len), - control->m_len -= CMSG_ALIGN(cm->cmsg_len)) { - cm = mtod(control, struct cmsghdr *); - if (cm->cmsg_len == 0 || cm->cmsg_len > control->m_len) + switch (optname) { + case IPV6_2292PKTINFO: + case IPV6_PKTINFO: + { + struct in6_pktinfo *pktinfo; + if (len != sizeof(struct in6_pktinfo)) return (EINVAL); - if (cm->cmsg_level != IPPROTO_IPV6) - continue; + pktinfo = (struct in6_pktinfo *)buf; /* - * XXX should check if RFC2292 API is mixed with 2292bis API + * An application can clear any sticky IPV6_PKTINFO option by + * doing a "regular" setsockopt with ipi6_addr being + * in6addr_any and ipi6_ifindex being zero. + * [RFC 3542, Section 6] */ - switch (cm->cmsg_type) { - case IPV6_PKTINFO: - if (cm->cmsg_len != CMSG_LEN(sizeof(struct in6_pktinfo))) - return (EINVAL); - if (needcopy) { - /* XXX: Is it really WAITOK? */ - opt->ip6po_pktinfo = - kmalloc(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); - } + 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_HOPLIMIT: - if (cm->cmsg_len != CMSG_LEN(sizeof(int))) - return (EINVAL); + if (uproto == IPPROTO_TCP && optname == IPV6_PKTINFO && + sticky && !IN6_IS_ADDR_UNSPECIFIED(&pktinfo->ipi6_addr)) { + return (EINVAL); + } - opt->ip6po_hlim = *(int *)CMSG_DATA(cm); - if (opt->ip6po_hlim < -1 || opt->ip6po_hlim > 255) - return (EINVAL); - break; + /* validate the interface index if specified. */ + if (pktinfo->ipi6_ifindex > if_index || + 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); + } - case IPV6_NEXTHOP: - if (!priv) - return (EPERM); + /* + * 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 (opt->ip6po_pktinfo == NULL) { + opt->ip6po_pktinfo = kmalloc(sizeof(*pktinfo), + M_IP6OPT, M_NOWAIT); + if (opt->ip6po_pktinfo == NULL) + return (ENOBUFS); + } + bcopy(pktinfo, opt->ip6po_pktinfo, sizeof(*pktinfo)); + break; + } - 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); + case IPV6_2292HOPLIMIT: + case IPV6_HOPLIMIT: + { + int *hlimp; - if (needcopy) { - opt->ip6po_nexthop = - kmalloc(*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; + /* + * RFC 3542 deprecated the usage of sticky IPV6_HOPLIMIT + * to simplify the ordering among hoplimit options. + */ + if (optname == IPV6_HOPLIMIT && sticky) + return (ENOPROTOOPT); - case IPV6_HOPOPTS: - { - struct ip6_hbh *hbh; - int hbhlen; + if (len != sizeof(int)) + return (EINVAL); + hlimp = (int *)buf; + if (*hlimp < -1 || *hlimp > 255) + return (EINVAL); - if (cm->cmsg_len < CMSG_LEN(sizeof(struct ip6_hbh))) - return (EINVAL); - hbh = (struct ip6_hbh *)CMSG_DATA(cm); - hbhlen = (hbh->ip6h_len + 1) << 3; - if (cm->cmsg_len != CMSG_LEN(hbhlen)) - return (EINVAL); + opt->ip6po_hlim = *hlimp; + break; + } - if (needcopy) { - opt->ip6po_hbh = - kmalloc(hbhlen, M_IP6OPT, M_WAITOK); - bcopy(hbh, opt->ip6po_hbh, hbhlen); - } else - opt->ip6po_hbh = hbh; + 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; } - case IPV6_DSTOPTS: + /* check if cmsg_len is large enough for sa_len */ + if (len < sizeof(struct sockaddr) || len < *buf) + return (EINVAL); + + switch (((struct sockaddr *)buf)->sa_family) { + case AF_INET6: { - struct ip6_dest *dest, **newdest; - int destlen; + struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)buf; + //int error; - if (cm->cmsg_len < CMSG_LEN(sizeof(struct ip6_dest))) + if (sa6->sin6_len != sizeof(struct sockaddr_in6)) return (EINVAL); - dest = (struct ip6_dest *)CMSG_DATA(cm); - destlen = (dest->ip6d_len + 1) << 3; - if (cm->cmsg_len != CMSG_LEN(destlen)) + + if (IN6_IS_ADDR_UNSPECIFIED(&sa6->sin6_addr) || + IN6_IS_ADDR_MULTICAST(&sa6->sin6_addr)) { return (EINVAL); + } + 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); + opt->ip6po_nexthop = kmalloc(*buf, M_IP6OPT, M_NOWAIT); + if (opt->ip6po_nexthop == NULL) + return (ENOBUFS); + bcopy(buf, opt->ip6po_nexthop, *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); + opt->ip6po_hbh = kmalloc(hbhlen, M_IP6OPT, M_NOWAIT); + if (opt->ip6po_hbh == NULL) + return (ENOBUFS); + bcopy(hbh, opt->ip6po_hbh, hbhlen); + + break; + } + + case IPV6_2292DSTOPTS: + case IPV6_DSTOPTS: + case IPV6_RTHDRDSTOPTS: + { + struct ip6_dest *dest, **newdest = NULL; + int destlen; + if (!priv) + 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 cmsg types. + * 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. + * RFC3542 solved the ambiguity by introducing + * separate ancillary data or option types. */ if (opt->ip6po_rthdr == NULL) newdest = &opt->ip6po_dest1; else newdest = &opt->ip6po_dest2; + break; + case IPV6_RTHDRDSTOPTS: + newdest = &opt->ip6po_dest1; + break; + case IPV6_DSTOPTS: + newdest = &opt->ip6po_dest2; + break; + } - if (needcopy) { - *newdest = kmalloc(destlen, M_IP6OPT, M_WAITOK); - bcopy(dest, *newdest, destlen); - } else - *newdest = dest; + /* turn off the previous option, then set the new option. */ + ip6_clearpktopts(opt, optname); + *newdest = kmalloc(destlen, M_IP6OPT, M_NOWAIT); + if (*newdest == NULL) + return (ENOBUFS); + bcopy(dest, *newdest, destlen); - break; + break; + } + + case IPV6_2292RTHDR: + case IPV6_RTHDR: + { + struct ip6_rthdr *rth; + int rthlen; + + if (len == 0) { + ip6_clearpktopts(opt, IPV6_RTHDR); + break; /* just remove the option */ } - case IPV6_RTHDR: - { - struct ip6_rthdr *rth; - int rthlen; + /* message length validation */ + if (len < sizeof(struct ip6_rthdr)) + return (EINVAL); + rth = (struct ip6_rthdr *)buf; + rthlen = (rth->ip6r_len + 1) << 3; + if (len != rthlen) + return (EINVAL); - if (cm->cmsg_len < CMSG_LEN(sizeof(struct ip6_rthdr))) - 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) { + default: + return (EINVAL); /* not supported */ + } - switch (rth->ip6r_type) { - case IPV6_RTHDR_TYPE_0: - /* must contain one addr */ - if (rth->ip6r_len == 0) - return (EINVAL); - /* length must be even */ - if (rth->ip6r_len % 2) - return (EINVAL); - if (rth->ip6r_len / 2 != rth->ip6r_segleft) - return (EINVAL); - break; - default: - return (EINVAL); /* not supported */ - } + /* turn off the previous option */ + ip6_clearpktopts(opt, IPV6_RTHDR); + opt->ip6po_rthdr = kmalloc(rthlen, M_IP6OPT, M_NOWAIT); + if (opt->ip6po_rthdr == NULL) + return (ENOBUFS); + bcopy(rth, opt->ip6po_rthdr, rthlen); - if (needcopy) { - opt->ip6po_rthdr = kmalloc(rthlen, M_IP6OPT, - M_WAITOK); - bcopy(rth, opt->ip6po_rthdr, rthlen); - } else - opt->ip6po_rthdr = rth; + break; + } - break; + case IPV6_USE_MIN_MTU: + if (len != sizeof(int)) + return (EINVAL); + minmtupolicy = *(int *)buf; + if (minmtupolicy != IP6PO_MINMTU_MCASTONLY && + minmtupolicy != IP6PO_MINMTU_DISABLE && + minmtupolicy != IP6PO_MINMTU_ALL) { + return (EINVAL); } + opt->ip6po_minmtu = minmtupolicy; + break; - default: - return (ENOPROTOOPT); + case IPV6_DONTFRAG: + if (len != sizeof(int)) + return (EINVAL); + + if (uproto == IPPROTO_TCP || *(int *)buf == 0) { + /* + * we ignore this option for TCP sockets. + * (RFC3542 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); +} + + +/* + * Set IPv6 outgoing packet options based on advanced API. + */ +int +ip6_setpktoptions(struct mbuf *control, struct ip6_pktopts *opt, + struct ip6_pktopts *stickyopt, int uproto, int priv) +{ + struct cmsghdr *cm = NULL; + + if (control == NULL || opt == NULL) + return (EINVAL); + + init_ip6pktopts(opt); + + /* + * XXX: Currently, we assume all the optional information is stored + * in a single mbuf. + */ + if (stickyopt) { + int error; + + /* + * If stickyopt is provided, make a local copy of the options + * for this particular packet, then override them by ancillary + * objects. + * XXX: copypktopts() does not copy the cached route to a next + * hop (if any). This is not very good in terms of efficiency, + * but we can allow this since this option should be rarely + * used. + */ + if ((error = copypktopts(opt, stickyopt, M_NOWAIT)) != 0) + return (error); + } + + /* + * XXX: Currently, we assume all the optional information is stored + * in a single mbuf. + */ + if (control->m_next) + return (EINVAL); + + for (; control->m_len; control->m_data += CMSG_ALIGN(cm->cmsg_len), + control->m_len -= CMSG_ALIGN(cm->cmsg_len)) { + int error; + + if (control->m_len < CMSG_LEN(0)) + return (EINVAL); + + cm = mtod(control, struct cmsghdr *); + if (cm->cmsg_len == 0 || cm->cmsg_len > control->m_len) + return (EINVAL); + if (cm->cmsg_level != IPPROTO_IPV6) + continue; + + error = ip6_setpktoption(cm->cmsg_type, CMSG_DATA(cm), + cm->cmsg_len - CMSG_LEN(0), opt, 0, 1, uproto, priv); + if (error) + return (error); } return (0); diff --git a/sys/netinet6/ip6_var.h b/sys/netinet6/ip6_var.h index bd9f9dd5c0..583781030b 100644 --- a/sys/netinet6/ip6_var.h +++ b/sys/netinet6/ip6_var.h @@ -1,5 +1,5 @@ /* $FreeBSD: src/sys/netinet6/ip6_var.h,v 1.2.2.4 2003/01/23 21:06:47 sam Exp $ */ -/* $DragonFly: src/sys/netinet6/ip6_var.h,v 1.12 2007/05/07 12:40:30 hasso Exp $ */ +/* $DragonFly: src/sys/netinet6/ip6_var.h,v 1.13 2008/09/04 09:08:22 hasso Exp $ */ /* $KAME: ip6_var.h,v 1.62 2001/05/03 14:51:48 itojun Exp $ */ /* @@ -138,6 +138,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 */ @@ -145,7 +153,8 @@ 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 */ @@ -157,12 +166,32 @@ struct ip6_pktopts { /* Destination options header (after a routing header) */ struct ip6_dest *ip6po_dest2; + /* + * below fields are introduced in RFC3542 + */ + 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 are 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 */ }; /* * Control options for incoming packets */ - struct ip6stat { u_quad_t ip6s_total; /* total packets received */ u_quad_t ip6s_tooshort; /* packet too short */ @@ -341,9 +370,12 @@ int ip6_output (struct mbuf *, struct ip6_pktopts *, struct ip6_moptions *, struct ifnet **, struct inpcb *); int ip6_ctloutput (struct socket *, struct sockopt *sopt); +int ip6_raw_ctloutput (struct socket *, struct sockopt *); void init_ip6pktopts (struct ip6_pktopts *); -int ip6_setpktoptions (struct mbuf *, struct ip6_pktopts *, int, int); -void ip6_clearpktopts (struct ip6_pktopts *, int, int); +int +ip6_setpktoptions(struct mbuf *, struct ip6_pktopts *, + struct ip6_pktopts *, int , int); +void ip6_clearpktopts (struct ip6_pktopts *, int); struct ip6_pktopts *ip6_copypktopts (struct ip6_pktopts *, int); int ip6_optlen (struct inpcb *); diff --git a/sys/netinet6/raw_ip6.c b/sys/netinet6/raw_ip6.c index 0293864c5e..34db51b0bc 100644 --- a/sys/netinet6/raw_ip6.c +++ b/sys/netinet6/raw_ip6.c @@ -27,7 +27,7 @@ * SUCH DAMAGE. * * $FreeBSD: src/sys/netinet6/raw_ip6.c,v 1.7.2.7 2003/01/24 05:11:35 sam Exp $ - * $DragonFly: src/sys/netinet6/raw_ip6.c,v 1.26 2008/05/17 20:33:36 dillon Exp $ + * $DragonFly: src/sys/netinet6/raw_ip6.c,v 1.27 2008/09/04 09:08:22 hasso Exp $ */ /* @@ -338,7 +338,9 @@ rip6_output(struct mbuf *m, struct socket *so, ...) priv = 1; dst = &dstsock->sin6_addr; if (control) { - if ((error = ip6_setpktoptions(control, &opt, priv, 0)) != 0) + if ((error = ip6_setpktoptions(control, &opt, + in6p->in6p_outputopts, + so->so_proto->pr_protocol, priv)) != 0) goto bad; optp = &opt; } else @@ -477,7 +479,7 @@ freectl: RTFREE(optp->ip6po_route.ro_rt); if (control) { if (optp == &opt) - ip6_clearpktopts(optp, 0, -1); + ip6_clearpktopts(optp, -1); m_freem(control); } return (error); @@ -514,6 +516,9 @@ rip6_ctloutput(struct socket *so, struct sockopt *sopt) case MRT6_PIM: error = ip6_mrouter_get(so, sopt); break; + case IPV6_CHECKSUM: + error = ip6_raw_ctloutput(so, sopt); + break; default: error = ip6_ctloutput(so, sopt); break; @@ -531,6 +536,9 @@ rip6_ctloutput(struct socket *so, struct sockopt *sopt) case MRT6_PIM: error = ip6_mrouter_set(so, sopt); break; + case IPV6_CHECKSUM: + error = ip6_raw_ctloutput(so, sopt); + break; default: error = ip6_ctloutput(so, sopt); break; diff --git a/sys/netinet6/route6.c b/sys/netinet6/route6.c index a912b59e6f..4c993b8d72 100644 --- a/sys/netinet6/route6.c +++ b/sys/netinet6/route6.c @@ -1,5 +1,5 @@ /* $FreeBSD: src/sys/netinet6/route6.c,v 1.1.2.5 2003/01/23 21:06:47 sam Exp $ */ -/* $DragonFly: src/sys/netinet6/route6.c,v 1.9 2007/05/07 13:00:16 hasso Exp $ */ +/* $DragonFly: src/sys/netinet6/route6.c,v 1.10 2008/09/04 09:08:22 hasso Exp $ */ /* $KAME: route6.c,v 1.24 2001/03/14 03:07:05 itojun Exp $ */ /* @@ -49,11 +49,6 @@ #include -#if 0 -static int ip6_rthdr0 (struct mbuf *, struct ip6_hdr *, - struct ip6_rthdr0 *); -#endif - int route6_input(struct mbuf **mp, int *offp, int proto) /* proto is unused */ { @@ -76,50 +71,6 @@ route6_input(struct mbuf **mp, int *offp, int proto) /* proto is unused */ #endif switch (rh->ip6r_type) { -#if 0 - /* - * See http://www.secdev.org/conf/IPv6_RH_security-csw07.pdf - * for why IPV6_RTHDR_TYPE_0 is baned here. - * - * We return ICMPv6 parameter problem so that innocent people - * (not an attacker) would notice about the use of IPV6_RTHDR_TYPE_0. - * Since there's no amplification, and ICMPv6 error will be rate- - * controlled, it shouldn't cause any problem. - * If you are concerned about this, you may want to use the following - * code fragment: - * - * case IPV6_RTHDR_TYPE_0: - * m_freem(m); - * return (IPPROTO_DONE); - */ - case IPV6_RTHDR_TYPE_0: - rhlen = (rh->ip6r_len + 1) << 3; -#ifndef PULLDOWN_TEST - /* - * note on option length: - * due to IP6_EXTHDR_CHECK assumption, we cannot handle - * very big routing header (max rhlen == 2048). - */ - IP6_EXTHDR_CHECK(m, off, rhlen, IPPROTO_DONE); -#else - /* - * note on option length: - * maximum rhlen: 2048 - * max mbuf m_pulldown can handle: MCLBYTES == usually 2048 - * so, here we are assuming that m_pulldown can handle - * rhlen == 2048 case. this may not be a good thing to - * assume - we may want to avoid pulling it up altogether. - */ - IP6_EXTHDR_GET(rh, struct ip6_rthdr *, m, off, rhlen); - if (rh == NULL) { - ip6stat.ip6s_tooshort++; - return IPPROTO_DONE; - } -#endif - if (ip6_rthdr0(m, ip6, (struct ip6_rthdr0 *)rh)) - return (IPPROTO_DONE); - break; -#endif default: /* unknown routing type */ if (rh->ip6r_segleft == 0) { @@ -135,93 +86,3 @@ route6_input(struct mbuf **mp, int *offp, int proto) /* proto is unused */ *offp += rhlen; return (rh->ip6r_nxt); } - -#if 0 -/* - * Type0 routing header processing - * - * RFC2292 backward compatibility warning: no support for strict/loose bitmap, - * as it was dropped between RFC1883 and RFC2460. - */ -static int -ip6_rthdr0(struct mbuf *m, struct ip6_hdr *ip6, struct ip6_rthdr0 *rh0) -{ - int addrs, index; - struct in6_addr *nextaddr, tmpaddr; - - if (rh0->ip6r0_segleft == 0) - return (0); - - if (rh0->ip6r0_len % 2 -#ifdef COMPAT_RFC1883 - || rh0->ip6r0_len > 46 -#endif - ) { - /* - * Type 0 routing header can't contain more than 23 addresses. - * RFC 2462: this limitation was removed since strict/loose - * bitmap field was deleted. - */ - ip6stat.ip6s_badoptions++; - icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, - (caddr_t)&rh0->ip6r0_len - (caddr_t)ip6); - return (-1); - } - - if ((addrs = rh0->ip6r0_len / 2) < rh0->ip6r0_segleft) { - ip6stat.ip6s_badoptions++; - icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, - (caddr_t)&rh0->ip6r0_segleft - (caddr_t)ip6); - return (-1); - } - - index = addrs - rh0->ip6r0_segleft; - rh0->ip6r0_segleft--; - /* note that ip6r0_addr does not exist in RFC2292bis */ - nextaddr = rh0->ip6r0_addr + index; - - /* - * reject invalid addresses. be proactive about malicious use of - * IPv4 mapped/compat address. - * XXX need more checks? - */ - if (IN6_IS_ADDR_MULTICAST(nextaddr) || - IN6_IS_ADDR_UNSPECIFIED(nextaddr) || - IN6_IS_ADDR_V4MAPPED(nextaddr) || - IN6_IS_ADDR_V4COMPAT(nextaddr)) { - ip6stat.ip6s_badoptions++; - m_freem(m); - return (-1); - } - if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) || - IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_dst) || - IN6_IS_ADDR_V4MAPPED(&ip6->ip6_dst) || - IN6_IS_ADDR_V4COMPAT(&ip6->ip6_dst)) { - ip6stat.ip6s_badoptions++; - m_freem(m); - return (-1); - } - - /* - * Swap the IPv6 destination address and nextaddr. Forward the packet. - */ - tmpaddr = *nextaddr; - *nextaddr = ip6->ip6_dst; - if (IN6_IS_ADDR_LINKLOCAL(nextaddr)) - nextaddr->s6_addr16[1] = 0; - ip6->ip6_dst = tmpaddr; - if (IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst)) - ip6->ip6_dst.s6_addr16[1] = htons(m->m_pkthdr.rcvif->if_index); - -#ifdef COMPAT_RFC1883 - if (rh0->ip6r0_slmap[index / 8] & (1 << (7 - (index % 8)))) - ip6_forward(m, IPV6_SRCRT_NEIGHBOR); - else - ip6_forward(m, IPV6_SRCRT_NOTNEIGHBOR); -#else - ip6_forward(m, 1); -#endif - - return (-1); /* m would be freed in ip6_forward() */ -} -#endif diff --git a/sys/netinet6/udp6_output.c b/sys/netinet6/udp6_output.c index b5a40af431..bdb93c0bba 100644 --- a/sys/netinet6/udp6_output.c +++ b/sys/netinet6/udp6_output.c @@ -1,5 +1,5 @@ /* $FreeBSD: src/sys/netinet6/udp6_output.c,v 1.1.2.6 2003/01/23 21:06:47 sam Exp $ */ -/* $DragonFly: src/sys/netinet6/udp6_output.c,v 1.8 2006/12/29 18:02:56 victor Exp $ */ +/* $DragonFly: src/sys/netinet6/udp6_output.c,v 1.9 2008/09/04 09:08:22 hasso Exp $ */ /* $KAME: udp6_output.c,v 1.31 2001/05/21 16:39:15 jinmei Exp $ */ /* @@ -139,7 +139,9 @@ udp6_output(struct in6pcb *in6p, struct mbuf *m, struct sockaddr *addr6, priv = !suser(td); /* 1 if privilaged, 0 if not */ if (control) { - if ((error = ip6_setpktoptions(control, &opt, priv, 0)) != 0) + if ((error = ip6_setpktoptions(control, &opt, + in6p->in6p_outputopts, + IPPROTO_UDP, priv)) != 0) goto release; in6p->in6p_outputopts = &opt; } @@ -304,7 +306,7 @@ release: releaseopt: if (control) { - ip6_clearpktopts(in6p->in6p_outputopts, 0, -1); + ip6_clearpktopts(in6p->in6p_outputopts, -1); in6p->in6p_outputopts = stickyopt; m_freem(control); } diff --git a/usr.bin/telnet/commands.c b/usr.bin/telnet/commands.c index c6eafbc888..e17a487360 100644 --- a/usr.bin/telnet/commands.c +++ b/usr.bin/telnet/commands.c @@ -32,7 +32,7 @@ * * @(#)commands.c 8.4 (Berkeley) 5/30/95 * $FreeBSD: src/usr.bin/telnet/commands.c,v 1.21.2.6 2002/11/30 05:35:13 eric Exp $ - * $DragonFly: src/usr.bin/telnet/commands.c,v 1.6 2007/05/18 17:05:12 dillon Exp $ + * $DragonFly: src/usr.bin/telnet/commands.c,v 1.7 2008/09/04 09:08:22 hasso Exp $ */ #include @@ -2568,6 +2568,7 @@ sourceroute(struct addrinfo *ai, char *arg, char **cpp, int *lenp, int *protop, #ifdef INET6 struct sockaddr_in6 *sin6; struct cmsghdr *cmsg; + struct ip6_rthdr *rth; #endif struct addrinfo hints, *res; int error; @@ -2610,11 +2611,18 @@ 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); + /* + *RFC3542 has obsoleted IPV6_PKTOPTIONS socket option. + */ +#ifdef COMPAT_RFC1883 /* XXX */ + cmsg = NULL; if (*cp != '@') return -1; *protop = IPPROTO_IPV6; *optp = IPV6_PKTOPTIONS; +#else + return -1; +#endif /* COMPAT_RFC1883 */ } else #endif { @@ -2685,9 +2693,7 @@ 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); + return(0); } else #endif { @@ -2703,13 +2709,13 @@ sourceroute(struct addrinfo *ai, char *arg, char **cpp, int *lenp, int *protop, * Check to make sure there is space for next address */ #ifdef INET6 +#ifdef COMPAT_RFC1883 /* XXX */ 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) + sizeof(struct ip6_rthdr)) > ep) return -1; } else +#endif /* COMPAT_RFC1883 */ #endif if (lsrp + 4 > ep) return -1; @@ -2717,8 +2723,9 @@ sourceroute(struct addrinfo *ai, char *arg, char **cpp, int *lenp, int *protop, } #ifdef INET6 if (res->ai_family == AF_INET6) { - inet6_rthdr_lasthop(cmsg, IPV6_RTHDR_LOOSE); - *lenp = cmsg->cmsg_len; +#ifdef COMPAT_RFC1883 /* XXX */ + *lenp = 0; +#endif /* COMPAT_RFC1883 */ } else #endif { diff --git a/usr.sbin/traceroute6/Makefile b/usr.sbin/traceroute6/Makefile index b06bcf3e6f..f4e0537eba 100644 --- a/usr.sbin/traceroute6/Makefile +++ b/usr.sbin/traceroute6/Makefile @@ -12,14 +12,14 @@ # LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE. # $FreeBSD: src/usr.sbin/traceroute6/Makefile,v 1.2.2.3 2002/07/23 07:48:27 ru Exp $ -# $DragonFly: src/usr.sbin/traceroute6/Makefile,v 1.2 2003/06/17 04:30:03 dillon Exp $ +# $DragonFly: src/usr.sbin/traceroute6/Makefile,v 1.3 2008/09/04 09:08:22 hasso Exp $ PROG= traceroute6 MAN= traceroute6.8 BINOWN= root BINMODE=4555 -CFLAGS+=-DINET6 -DIPSEC -DHAVE_POLL +CFLAGS+=-DINET6 -DIPSEC -DHAVE_POLL -DUSE_RFC3542 DPADD= ${LIBIPSEC} LDADD= -lipsec diff --git a/usr.sbin/traceroute6/traceroute6.c b/usr.sbin/traceroute6/traceroute6.c index 4ee1f813dc..57ae5b1c7d 100644 --- a/usr.sbin/traceroute6/traceroute6.c +++ b/usr.sbin/traceroute6/traceroute6.c @@ -68,7 +68,7 @@ * The Regents of the University of California. All rights reserved. * * $FreeBSD: src/usr.sbin/traceroute6/traceroute6.c,v 1.22 2008/02/10 21:06:38 dwmalone Exp $ - * $DragonFly: src/usr.sbin/traceroute6/traceroute6.c,v 1.8 2008/05/20 12:14:10 hasso Exp $ + * $DragonFly: src/usr.sbin/traceroute6/traceroute6.c,v 1.9 2008/09/04 09:08:22 hasso Exp $ */ /* @@ -331,10 +331,6 @@ u_long datalen; /* How much data */ #define ICMP6ECHOLEN 8 /* XXX: 2064 = 127(max hops in type 0 rthdr) * sizeof(ip6_hdr) + 16(margin) */ char rtbuf[2064]; -#ifdef USE_RFC2292BIS -struct ip6_rthdr *rth; -#endif -struct cmsghdr *cmsg; char *source = 0; char *hostname; @@ -423,33 +419,6 @@ main(int argc, char **argv) "traceroute6: unknown host %s\n", optarg); exit(1); } -#ifdef USE_RFC2292BIS - if (rth == NULL) { - /* - * XXX: We can't detect the number of - * intermediate nodes yet. - */ - if ((rth = inet6_rth_init((void *)rtbuf, - sizeof(rtbuf), IPV6_RTHDR_TYPE_0, - 0)) == NULL) { - fprintf(stderr, - "inet6_rth_init failed.\n"); - exit(1); - } - } - if (inet6_rth_add((void *)rth, - (struct in6_addr *)hp->h_addr)) { - fprintf(stderr, - "inet6_rth_add failed for %s\n", - optarg); - exit(1); - } -#else /* old advanced API */ - if (cmsg == NULL) - cmsg = inet6_rthdr_init(rtbuf, IPV6_RTHDR_TYPE_0); - inet6_rthdr_add(cmsg, (struct in6_addr *)hp->h_addr, - IPV6_RTHDR_LOOSE); -#endif freehostent(hp); break; case 'I': @@ -729,27 +698,6 @@ main(int argc, char **argv) if (options & SO_DONTROUTE) (void) setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE, (char *)&on, sizeof(on)); -#ifdef USE_RFC2292BIS - if (rth) {/* XXX: there is no library to finalize the header... */ - rth->ip6r_len = rth->ip6r_segleft * 2; - if (setsockopt(sndsock, IPPROTO_IPV6, IPV6_RTHDR, - (void *)rth, (rth->ip6r_len + 1) << 3)) { - fprintf(stderr, "setsockopt(IPV6_RTHDR): %s\n", - strerror(errno)); - exit(1); - } - } -#else /* old advanced API */ - if (cmsg != NULL) { - inet6_rthdr_lasthop(cmsg, IPV6_RTHDR_LOOSE); - if (setsockopt(sndsock, IPPROTO_IPV6, IPV6_PKTOPTIONS, - rtbuf, cmsg->cmsg_len) < 0) { - fprintf(stderr, "setsockopt(IPV6_PKTOPTIONS): %s\n", - strerror(errno)); - exit(1); - } - } -#endif /* USE_RFC2292BIS */ #ifdef IPSEC #ifdef IPSEC_POLICY_IPSEC /* @@ -815,9 +763,6 @@ main(int argc, char **argv) Nxt = Dst; Nxt.sin6_port = htons(DUMMY_PORT); - if (cmsg != NULL) - bcopy(inet6_rthdr_getaddr(cmsg, 1), &Nxt.sin6_addr, - sizeof(Nxt.sin6_addr)); if ((dummy = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { perror("socket"); exit(1);