? sys/netinet/tcp_ecn.c Index: sys/conf/std =================================================================== RCS file: /cvsroot/src/sys/conf/std,v retrieving revision 1.4 diff -u -p -r1.4 std --- sys/conf/std 11 Dec 2005 12:20:30 -0000 1.4 +++ sys/conf/std 29 Apr 2006 11:15:11 -0000 @@ -10,3 +10,4 @@ options VMSWAP # Swap device/file support options BUFQ_FCFS # First-come First-serve strategy options BUFQ_DISKSORT # Traditional min seek sort strategy +options RFC2292 # Previous version of Adv. Sockets API for IPv6 Index: distrib/sets/lists/comp/mi =================================================================== RCS file: /cvsroot/src/distrib/sets/lists/comp/mi,v retrieving revision 1.873 diff -u -p -r1.873 mi --- distrib/sets/lists/comp/mi 27 Apr 2006 20:41:58 -0000 1.873 +++ distrib/sets/lists/comp/mi 29 Apr 2006 11:15:19 -0000 @@ -3445,6 +3445,19 @@ ./usr/share/man/cat3/inet6_rthdr_reverse.0 comp-c-catman .cat ./usr/share/man/cat3/inet6_rthdr_segments.0 comp-c-catman .cat ./usr/share/man/cat3/inet6_rthdr_space.0 comp-c-catman .cat +./usr/share/man/cat3/inet6_opt_init.0 comp-c-catman .cat +./usr/share/man/cat3/inet6_opt_append.0 comp-c-catman .cat +./usr/share/man/cat3/inet6_opt_finish.0 comp-c-catman .cat +./usr/share/man/cat3/inet6_opt_set_val.0 comp-c-catman .cat +./usr/share/man/cat3/inet6_opt_next.0 comp-c-catman .cat +./usr/share/man/cat3/inet6_opt_find.0 comp-c-catman .cat +./usr/share/man/cat3/inet6_opt_get_val.0 comp-c-catman .cat +./usr/share/man/cat3/inet6_rth_space.0 comp-c-catman .cat +./usr/share/man/cat3/inet6_rth_init.0 comp-c-catman .cat +./usr/share/man/cat3/inet6_rth_add.0 comp-c-catman .cat +./usr/share/man/cat3/inet6_rth_reverse.0 comp-c-catman .cat +./usr/share/man/cat3/inet6_rth_segments.0 comp-c-catman .cat +./usr/share/man/cat3/inet6_rth_getaddr.0 comp-c-catman .cat ./usr/share/man/cat3/inet_addr.0 comp-c-catman .cat ./usr/share/man/cat3/inet_aton.0 comp-c-catman .cat ./usr/share/man/cat3/inet_lnaof.0 comp-c-catman .cat @@ -7492,6 +7505,19 @@ ./usr/share/man/man3/inet6_rthdr_reverse.3 comp-c-man .man ./usr/share/man/man3/inet6_rthdr_segments.3 comp-c-man .man ./usr/share/man/man3/inet6_rthdr_space.3 comp-c-man .man +./usr/share/man/man3/inet6_opt_init.3 comp-c-man .man +./usr/share/man/man3/inet6_opt_append.3 comp-c-man .man +./usr/share/man/man3/inet6_opt_finish.3 comp-c-man .man +./usr/share/man/man3/inet6_opt_set_val.3 comp-c-man .man +./usr/share/man/man3/inet6_opt_next.3 comp-c-man .man +./usr/share/man/man3/inet6_opt_find.3 comp-c-man .man +./usr/share/man/man3/inet6_opt_get_val.3 comp-c-man .man +./usr/share/man/man3/inet6_rth_space.3 comp-c-man .man +./usr/share/man/man3/inet6_rth_init.3 comp-c-man .man +./usr/share/man/man3/inet6_rth_add.3 comp-c-man .man +./usr/share/man/man3/inet6_rth_reverse.3 comp-c-man .man +./usr/share/man/man3/inet6_rth_segments.3 comp-c-man .man +./usr/share/man/man3/inet6_rth_getaddr.3 comp-c-man .man ./usr/share/man/man3/inet_addr.3 comp-c-man .man ./usr/share/man/man3/inet_aton.3 comp-c-man .man ./usr/share/man/man3/inet_lnaof.3 comp-c-man .man Index: lib/libc/include/namespace.h =================================================================== RCS file: /cvsroot/src/lib/libc/include/namespace.h,v retrieving revision 1.115 diff -u -p -r1.115 namespace.h --- lib/libc/include/namespace.h 17 Apr 2006 23:29:21 -0000 1.115 +++ lib/libc/include/namespace.h 29 Apr 2006 11:15:20 -0000 @@ -364,6 +364,13 @@ #define inet6_option_init _inet6_option_init #define inet6_option_next _inet6_option_next #define inet6_option_space _inet6_option_space +#define inet6_opt_init _inet6_opt_init +#define inet6_opt_append _inet6_opt_append +#define inet6_opt_finish _inet6_opt_finish +#define inet6_opt_set_val _inet6_opt_set_val +#define inet6_opt_next _inet6_opt_next +#define inet6_opt_find _inet6_opt_find +#define inet6_opt_get_val _inet6_opt_get_val #define inet6_rthdr_add _inet6_rthdr_add #define inet6_rthdr_getaddr _inet6_rthdr_getaddr #define inet6_rthdr_getflags _inet6_rthdr_getflags @@ -371,6 +378,12 @@ #define inet6_rthdr_lasthop _inet6_rthdr_lasthop #define inet6_rthdr_segments _inet6_rthdr_segments #define inet6_rthdr_space _inet6_rthdr_space +#define inet6_rth_space _inet6_rth_space +#define inet6_rth_init _inet6_rth_init +#define inet6_rth_add _inet6_rth_add +#define inet6_rth_reverse _inet6_rth_reverse +#define inet6_rth_segments _inet6_rth_segments +#define inet6_rth_getaddr _inet6_rth_getaddr #define inet_cidr_ntop _inet_cidr_ntop #define inet_cidr_pton _inet_cidr_pton #define inet_lnaof _inet_lnaof Index: lib/libc/net/Makefile.inc =================================================================== RCS file: /cvsroot/src/lib/libc/net/Makefile.inc,v retrieving revision 1.68 diff -u -p -r1.68 Makefile.inc --- lib/libc/net/Makefile.inc 11 Jan 2005 06:01:41 -0000 1.68 +++ lib/libc/net/Makefile.inc 29 Apr 2006 11:15:21 -0000 @@ -83,7 +83,8 @@ MLINKS+=resolver.3 dn_comp.3 resolver.3 # IPv6 MAN+= gai_strerror.3 getaddrinfo.3 getnameinfo.3 if_indextoname.3 \ - inet6_option_space.3 inet6_rthdr_space.3 + inet6_option_space.3 inet6_rthdr_space.3 \ + inet6_opt_init.3 inet6_rth_space.3 MLINKS+=getaddrinfo.3 freeaddrinfo.3 \ getifaddrs.3 freeifaddrs.3 \ if_indextoname.3 if_nametoindex.3 if_indextoname.3 if_nameindex.3 \ @@ -100,5 +101,16 @@ MLINKS+=getaddrinfo.3 freeaddrinfo.3 \ inet6_rthdr_space.3 inet6_rthdr_segments.3 \ inet6_rthdr_space.3 inet6_rthdr_getaddr.3 \ inet6_rthdr_space.3 inet6_rthdr_getflags.3 \ + inet6_opt_init.3 inet6_opt_append.3 \ + inet6_opt_init.3 inet6_opt_finish.3 \ + inet6_opt_init.3 inet6_opt_set_val.3 \ + inet6_opt_init.3 inet6_opt_next.3 \ + inet6_opt_init.3 inet6_opt_find.3 \ + inet6_opt_init.3 inet6_opt_get_val.3 \ + inet6_rth_space.3 inet6_rth_init.3 \ + inet6_rth_space.3 inet6_rth_add.3 \ + inet6_rth_space.3 inet6_rth_reverse.3 \ + inet6_rth_space.3 inet6_rth_segments.3 \ + inet6_rth_space.3 inet6_rth_getaddr.3 \ rcmd.3 rcmd_af.3 rcmd.3 iruserok_sa.3 rcmd.3 rresvport_af.3 \ rcmd.3 orcmd_af.3 Index: lib/libc/net/inet6_opt_init.3 =================================================================== RCS file: lib/libc/net/inet6_opt_init.3 diff -N lib/libc/net/inet6_opt_init.3 --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ lib/libc/net/inet6_opt_init.3 29 Apr 2006 11:15:21 -0000 @@ -0,0 +1,337 @@ +.\" $NetBSD$ +.\" $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. +.\" +.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" "socklen_t 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-Hope and Destination +options. +.\"This man page describes the functions specified in +.\"IETF Draft RFC3542 while the +.\".Xr inet6_options_space 3 +.\"man page documents the functions defined in RFC 2292. +.\"It is expected +.\"that this set of functions will supersede those in RFC 2292 but for +.\"the time being both APIs are retained. +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 +.Va 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 +.Va 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 to 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 +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 . +.Fa typep, lenp, +and +.Fa databufp +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. +.Fa extbuf +and +.Fa extlen +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 DIAGNOSTICS +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 HISTORY +The implementation first appeared in KAME advanced networking kit. +.Sh STANDARDS +The functions are documented in +.Dq Advanced Sockets API for IPv6 +.Pq RFC3542 . +.\" Index: lib/libc/net/inet6_rth_space.3 =================================================================== RCS file: lib/libc/net/inet6_rth_space.3 diff -N lib/libc/net/inet6_rth_space.3 --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ lib/libc/net/inet6_rth_space.3 29 Apr 2006 11:15:21 -0000 @@ -0,0 +1,223 @@ +.\" $NetBSD$ +.\" $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. +.\" +.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 +argumment. +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 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 EXAMPLES +RFC 3542 gives extensive examples in Section 21, Appendix B. +.Pp +KAME also provides examples in the 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 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. Index: lib/libc/net/ip6opt.c =================================================================== RCS file: /cvsroot/src/lib/libc/net/ip6opt.c,v retrieving revision 1.10 diff -u -p -r1.10 ip6opt.c --- lib/libc/net/ip6opt.c 29 Nov 2005 03:11:59 -0000 1.10 +++ lib/libc/net/ip6opt.c 29 Apr 2006 11:15:21 -0000 @@ -53,6 +53,13 @@ __weak_alias(inet6_option_find,_inet6_op __weak_alias(inet6_option_init,_inet6_option_init) __weak_alias(inet6_option_next,_inet6_option_next) __weak_alias(inet6_option_space,_inet6_option_space) +__weak_alias(inet6_opt_init, _inet6_opt_init) +__weak_alias(inet6_opt_append, _inet6_opt_append) +__weak_alias(inet6_opt_finish, _inet6_opt_finish) +__weak_alias(inet6_opt_set_val, _inet6_opt_set_val) +__weak_alias(inet6_opt_next, _inet6_opt_next) +__weak_alias(inet6_opt_find, _inet6_opt_find) +__weak_alias(inet6_opt_get_val, _inet6_opt_get_val) #endif static int ip6optlen(u_int8_t *opt, u_int8_t *lim); @@ -153,6 +160,7 @@ inet6_option_append(cmsg, typep, multx, padlen = (((off % multx) + (multx - 1)) & ~(multx - 1)) - (off % multx); padlen += plusy; + padlen %= multx; /* keep the pad as short as possible */ /* insert padding */ inet6_insert_padopt(bp, padlen); cmsg->cmsg_len += padlen; @@ -232,6 +240,7 @@ inet6_option_alloc(cmsg, datalen, multx, padlen = (((off % multx) + (multx - 1)) & ~(multx - 1)) - (off % multx); padlen += plusy; + padlen %= multx; /* keep the pad as short as possible */ /* insert padding */ inet6_insert_padopt(bp, padlen); cmsg->cmsg_len += padlen; @@ -428,3 +437,222 @@ inet6_insert_padopt(u_char *p, size_t le return; } } + +/* + * The following functions are defined in RFC3542, which is a successor + * of RFC2292. + */ + +int +inet6_opt_init(void *extbuf, socklen_t extlen) +{ + struct ip6_ext *ext = (struct ip6_ext *)extbuf; + + if (extlen % 8) + return (-1); + + if (ext) { + if (extlen == 0) + return (-1); + ext->ip6e_len = (extlen >> 3) - 1; + } + + return (2); /* sizeof the next and the length fields */ +} + +int +inet6_opt_append(void *extbuf, socklen_t extlen, int offset, u_int8_t type, + socklen_t len, u_int8_t align, void **databufp) +{ + int currentlen = offset; + size_t padlen = 0; + + /* + * The option type must have a value from 2 to 255, inclusive. + * (0 and 1 are reserved for the Pad1 and PadN options, respectively.) + */ + if (type < 2) + return (-1); + + /* + * The option data length must have a value between 0 and 255, + * inclusive, and is the length of the option data that follows. + */ + if (len > 255) + return (-1); + + /* + * The align parameter must have a value of 1, 2, 4, or 8. + * The align value can not exceed the value of len. + */ + if (align != 1 && align != 2 && align != 4 && align != 8) + return (-1); + if (align > len) + return (-1); + + /* Calculate the padding length. */ + currentlen += 2 + len; /* 2 means "type + len" */ + if (currentlen % align) + padlen = align - (currentlen % align); + + /* The option must fit in the extension header buffer. */ + currentlen += padlen; + if (extlen && /* XXX: right? */ + currentlen > extlen) + return (-1); + + if (extbuf) { + u_int8_t *optp = (u_int8_t *)extbuf + offset; + + if (padlen == 1) { + /* insert a Pad1 option */ + *optp = IP6OPT_PAD1; + optp++; + } else if (padlen > 0) { + /* insert a PadN option for alignment */ + *optp++ = IP6OPT_PADN; + *optp++ = padlen - 2; + memset(optp, 0, padlen - 2); + optp += (padlen - 2); + } + + *optp++ = type; + *optp++ = len; + + *databufp = optp; + } + + return (currentlen); +} + +int +inet6_opt_finish(void *extbuf, socklen_t extlen, int offset) +{ + int updatelen = offset > 0 ? (1 + ((offset - 1) | 7)) : 0;; + + if (extbuf) { + u_int8_t *padp; + size_t padlen = updatelen - offset; + + if (updatelen > extlen) + return (-1); + + padp = (u_int8_t *)extbuf + offset; + if (padlen == 1) + *padp = IP6OPT_PAD1; + else if (padlen > 0) { + *padp++ = IP6OPT_PADN; + *padp++ = (padlen - 2); + memset(padp, 0, padlen - 2); + } + } + + return (updatelen); +} + +int +inet6_opt_set_val(void *databuf, int offset, void *val, socklen_t vallen) +{ + + memcpy((u_int8_t *)databuf + offset, val, vallen); + return (offset + vallen); +} + +int +inet6_opt_next(void *extbuf, socklen_t extlen, int offset, u_int8_t *typep, + socklen_t *lenp, void **databufp) +{ + u_int8_t *optp, *lim; + int optlen; + + /* Validate extlen. XXX: is the variable really necessary?? */ + if (extlen == 0 || (extlen % 8)) + return (-1); + lim = (u_int8_t *)extbuf + extlen; + + /* + * If this is the first time this function called for this options + * header, simply return the 1st option. + * Otherwise, search the option list for the next option. + */ + if (offset == 0) + optp = (u_int8_t *)(void *)((struct ip6_hbh *)extbuf + 1); + else + optp = (u_int8_t *)extbuf + offset; + + /* Find the next option skipping any padding options. */ + while (optp < lim) { + switch(*optp) { + case IP6OPT_PAD1: + optp++; + break; + case IP6OPT_PADN: + if ((optlen = ip6optlen(optp, lim)) == 0) + goto optend; + optp += optlen; + break; + default: /* found */ + if ((optlen = ip6optlen(optp, lim)) == 0) + goto optend; + *typep = *optp; + *lenp = optlen - 2; + *databufp = optp + 2; + return (optp + optlen - (u_int8_t *)extbuf); + } + } + + optend: + *databufp = NULL; /* for safety */ + return (-1); +} + +int +inet6_opt_find(void *extbuf, socklen_t extlen, int offset, u_int8_t type, + socklen_t *lenp, void **databufp) +{ + u_int8_t *optp, *lim; + int optlen; + + /* Validate extlen. XXX: is the variable really necessary?? */ + if (extlen == 0 || (extlen % 8)) + return (-1); + lim = (u_int8_t *)extbuf + extlen; + + /* + * If this is the first time this function called for this options + * header, simply return the 1st option. + * Otherwise, search the option list for the next option. + */ + if (offset == 0) + optp = (u_int8_t *)(void *)((struct ip6_hbh *)extbuf + 1); + else + optp = (u_int8_t *)extbuf + offset; + + /* Find the specified option */ + while (optp < lim) { + if ((optlen = ip6optlen(optp, lim)) == 0) + goto optend; + + if (*optp == type) { /* found */ + *lenp = optlen - 2; + *databufp = optp + 2; + return (optp + optlen - (u_int8_t *)extbuf); + } + + optp += optlen; + } + + optend: + *databufp = NULL; /* for safety */ + return (-1); +} + +int +inet6_opt_get_val(void *databuf, int offset, void *val, socklen_t vallen) +{ + + /* we can't assume alignment here */ + memcpy(val, (u_int8_t *)databuf + offset, vallen); + + return (offset + vallen); +} Index: lib/libc/net/rthdr.c =================================================================== RCS file: /cvsroot/src/lib/libc/net/rthdr.c,v retrieving revision 1.15 diff -u -p -r1.15 rthdr.c --- lib/libc/net/rthdr.c 29 Nov 2005 03:12:00 -0000 1.15 +++ lib/libc/net/rthdr.c 29 Apr 2006 11:15:21 -0000 @@ -54,8 +54,18 @@ __weak_alias(inet6_rthdr_init,_inet6_rth __weak_alias(inet6_rthdr_lasthop,_inet6_rthdr_lasthop) __weak_alias(inet6_rthdr_segments,_inet6_rthdr_segments) __weak_alias(inet6_rthdr_space,_inet6_rthdr_space) +__weak_alias(inet6_rth_space, _inet6_rth_space) +__weak_alias(inet6_rth_init, _inet6_rth_init) +__weak_alias(inet6_rth_add, _inet6_rth_add) +__weak_alias(inet6_rth_reverse, _inet6_rth_reverse) +__weak_alias(inet6_rth_segments, _inet6_rth_segments) +__weak_alias(inet6_rth_getaddr, _inet6_rth_getaddr) #endif +/* + * RFC2292 API + */ + size_t inet6_rthdr_space(type, seg) int type, seg; @@ -89,7 +99,12 @@ inet6_rthdr_init(bp, type) switch (type) { case IPV6_RTHDR_TYPE_0: +#ifdef COMPAT_RFC2292 + ch->cmsg_len = CMSG_LEN(sizeof(struct ip6_rthdr0) - + sizeof(struct in6_addr)); +#else ch->cmsg_len = CMSG_LEN(sizeof(struct ip6_rthdr0)); +#endif (void)memset(rthdr, 0, sizeof(struct ip6_rthdr0)); rthdr->ip6r_type = IPV6_RTHDR_TYPE_0; return (ch); @@ -115,10 +130,12 @@ inet6_rthdr_add(cmsg, addr, flags) case IPV6_RTHDR_TYPE_0: { struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)(void *)rthdr; - if (flags != IPV6_RTHDR_LOOSE) + if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT) return (-1); if (rt0->ip6r0_segleft == 23) return (-1); + if (flags != IPV6_RTHDR_LOOSE) + return (-1); rt0->ip6r0_segleft++; (void)memcpy(((caddr_t)(void *)rt0) + ((rt0->ip6r0_len + 1) << 3), addr, sizeof(struct in6_addr)); @@ -148,10 +165,10 @@ inet6_rthdr_lasthop(cmsg, flags) case IPV6_RTHDR_TYPE_0: { struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)(void *)rthdr; - if (flags != IPV6_RTHDR_LOOSE) - return (-1); if (rt0->ip6r0_segleft > 23) return (-1); + if (flags != IPV6_RTHDR_LOOSE) + return (-1); break; } default: @@ -221,7 +238,11 @@ inet6_rthdr_getaddr(cmsg, idx) naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr); if (idx <= 0 || naddr < idx) return NULL; +#ifdef COMPAT_RFC2292 + return ((struct in6_addr *)(void *)(rt0 + 1)) + idx - 1; +#else return ((struct in6_addr *)(void *)(rt0 + 1)) + idx; +#endif } default: @@ -259,3 +280,183 @@ inet6_rthdr_getflags(cmsg, idx) return (-1); } } + +/* + * RFC3542 (2292bis) API + */ + +socklen_t +inet6_rth_space(int type, int segments) +{ + switch (type) { + case IPV6_RTHDR_TYPE_0: + return (((segments * 2) + 1) << 3); + default: + return (0); /* type not suppported */ + } +} + +void * +inet6_rth_init(void *bp, socklen_t bp_len, int type, int segments) +{ + struct ip6_rthdr *rth; + struct ip6_rthdr0 *rth0; + + _DIAGASSERT(bp != NULL); + + rth = (struct ip6_rthdr *)bp; + + switch (type) { + case IPV6_RTHDR_TYPE_0: + /* length validation */ + if (bp_len < inet6_rth_space(IPV6_RTHDR_TYPE_0, segments)) + return (NULL); + + memset(bp, 0, bp_len); + rth0 = (struct ip6_rthdr0 *)(void *)rth; + rth0->ip6r0_len = segments * 2; + rth0->ip6r0_type = IPV6_RTHDR_TYPE_0; + rth0->ip6r0_segleft = 0; + rth0->ip6r0_reserved = 0; + break; + default: + return (NULL); /* type not supported */ + } + + return (bp); +} + +int +inet6_rth_add(void *bp, const struct in6_addr *addr) +{ + struct ip6_rthdr *rth; + struct ip6_rthdr0 *rth0; + struct in6_addr *nextaddr; + + _DIAGASSERT(bp != NULL); + + rth = (struct ip6_rthdr *)bp; + + switch (rth->ip6r_type) { + case IPV6_RTHDR_TYPE_0: + rth0 = (struct ip6_rthdr0 *)(void *)rth; + nextaddr = (struct in6_addr *)(void *)(rth0 + 1) + + rth0->ip6r0_segleft; + *nextaddr = *addr; + rth0->ip6r0_segleft++; + break; + default: + return (-1); /* type not supported */ + } + + return (0); +} + +int +inet6_rth_reverse(const void *in, void *out) +{ + const struct ip6_rthdr *rth_in; + const struct ip6_rthdr0 *rth0_in; + struct ip6_rthdr0 *rth0_out; + int i, segments; + + _DIAGASSERT(in != NULL); + _DIAGASSERT(out != NULL); + + rth_in = (const struct ip6_rthdr *)in; + + switch (rth_in->ip6r_type) { + case IPV6_RTHDR_TYPE_0: + rth0_in = (const struct ip6_rthdr0 *)in; + rth0_out = (struct ip6_rthdr0 *)out; + + /* parameter validation XXX too paranoid? */ + if (rth0_in->ip6r0_len % 2) + return (-1); + segments = rth0_in->ip6r0_len / 2; + + /* we can't use memcpy here, since in and out may overlap */ + memmove((void *)rth0_out, (const void *)rth0_in, + (unsigned int)(((rth0_in->ip6r0_len) + 1) << 3)); + rth0_out->ip6r0_segleft = segments; + + /* reverse the addresses */ + for (i = 0; i < segments / 2; i++) { + struct in6_addr addr_tmp, *addr1, *addr2; + + addr1 = (struct in6_addr *)(void *)(rth0_out + 1) + i; + addr2 = (struct in6_addr *)(void *)(rth0_out + 1) + + (segments - i - 1); + addr_tmp = *addr1; + *addr1 = *addr2; + *addr2 = addr_tmp; + } + + break; + default: + return (-1); /* type not supported */ + } + + return (0); +} + +int +inet6_rth_segments(const void *bp) +{ + const struct ip6_rthdr *rh; + const struct ip6_rthdr0 *rh0; + unsigned int addrs; + + _DIAGASSERT(bp != NULL); + + rh = (const struct ip6_rthdr *)bp; + + switch (rh->ip6r_type) { + case IPV6_RTHDR_TYPE_0: + rh0 = (const struct ip6_rthdr0 *)bp; + + /* + * Validation for a type-0 routing header. + * Is this too strict? + */ + if ((rh0->ip6r0_len % 2) != 0 || + (addrs = (rh0->ip6r0_len / 2)) < rh0->ip6r0_segleft) + return (-1); + + return (addrs); + default: + return (-1); /* unknown type */ + } +} + +struct in6_addr * +inet6_rth_getaddr(const void *bp, int idx) +{ + const struct ip6_rthdr *rh; + const struct ip6_rthdr0 *rh0; + unsigned int addrs; + + _DIAGASSERT(bp != NULL); + + rh = (const struct ip6_rthdr *)bp; + + switch (rh->ip6r_type) { + case IPV6_RTHDR_TYPE_0: + rh0 = (const struct ip6_rthdr0 *)bp; + + /* + * Validation for a type-0 routing header. + * Is this too strict? + */ + if ((rh0->ip6r0_len % 2) != 0 || + (addrs = (rh0->ip6r0_len / 2)) < rh0->ip6r0_segleft) + return (NULL); + + if (idx < 0 || addrs <= idx) + return (NULL); + + return (((struct in6_addr *)(void *)__UNCONST(rh0 + 1)) + idx); + default: + return (NULL); /* unknown type */ + } +} Index: sbin/ping6/Makefile =================================================================== RCS file: /cvsroot/src/sbin/ping6/Makefile,v retrieving revision 1.9 diff -u -p -r1.9 Makefile --- sbin/ping6/Makefile 27 Jun 2005 01:00:06 -0000 1.9 +++ sbin/ping6/Makefile 29 Apr 2006 11:15:22 -0000 @@ -8,6 +8,7 @@ BINMODE=4555 CPPFLAGS+=-DINET6 CPPFLAGS+=-DIPSEC +CPPFLAGS+=-DUSE_RFC3542 LDADD+= -lipsec -lm DPADD+= ${LIBIPSEC} ${LIBM} Index: sbin/ping6/ping6.8 =================================================================== RCS file: /cvsroot/src/sbin/ping6/ping6.8,v retrieving revision 1.25 diff -u -p -r1.25 ping6.8 --- sbin/ping6/ping6.8 7 Sep 2003 16:22:23 -0000 1.25 +++ sbin/ping6/ping6.8 29 Apr 2006 11:15:22 -0000 @@ -39,7 +39,7 @@ packets to network hosts .Sh SYNOPSIS .Nm ping6 .\" without IPsec, or new IPsec -.Op Fl dfHnNqRtvwW +.Op Fl dfHmnNqRtvwW .\" old IPsec .\" .Op Fl AdEfnNqRtvwW .Bk -words @@ -195,6 +195,16 @@ is specified, sends that many packets as fast as possible before falling into its normal mode of behavior. Only the super-user may use this option. +.It Fl m +By default, +.Nm +asks the kernel to fragment packets to fit into the minimum IPv6 MTU. +.Fl m +will suppress the behavior in the following two levels: +when the option is specified once, the behavior will be disabled for +unicast packets. +When the option is specified more than once, it will be disabled for both +unicast and multicast packets. .It Fl n Numeric output only. No attempt will be made to lookup symbolic names from addresses in the reply. Index: sbin/ping6/ping6.c =================================================================== RCS file: /cvsroot/src/sbin/ping6/ping6.c,v retrieving revision 1.65 diff -u -p -r1.65 ping6.c --- sbin/ping6/ping6.c 17 Mar 2006 02:31:03 -0000 1.65 +++ sbin/ping6/ping6.c 29 Apr 2006 11:15:23 -0000 @@ -299,7 +299,7 @@ main(int argc, char *argv[]) #endif int usepktinfo = 0; struct in6_pktinfo *pktinfo = NULL; -#ifdef USE_RFC2292BIS +#ifdef USE_RFC3542 struct ip6_rthdr *rthdr = NULL; #endif #ifdef IPSEC_POLICY_IPSEC @@ -566,7 +566,7 @@ main(int argc, char *argv[]) } if (argc > 1) { -#ifdef IPV6_RECVRTHDR /* 2292bis */ +#ifdef IPV6_RECVRTHDR /* RFC3542 */ rthlen = CMSG_SPACE(inet6_rth_space(IPV6_RTHDR_TYPE_0, argc - 1)); #else /* RFC2292 */ @@ -872,11 +872,11 @@ main(int argc, char *argv[]) if (argc > 1) { /* some intermediate addrs are specified */ int hops, error; -#ifdef USE_RFC2292BIS +#ifdef USE_RFC3542 int rthdrlen; #endif -#ifdef USE_RFC2292BIS +#ifdef USE_RFC3542 rthdrlen = inet6_rth_space(IPV6_RTHDR_TYPE_0, argc - 1); scmsgp->cmsg_len = CMSG_LEN(rthdrlen); scmsgp->cmsg_level = IPPROTO_IPV6; @@ -890,7 +890,7 @@ main(int argc, char *argv[]) if ((scmsgp = (struct cmsghdr *)inet6_rthdr_init(scmsgp, IPV6_RTHDR_TYPE_0)) == 0) errx(1, "can't initialize rthdr"); -#endif /* USE_RFC2292BIS */ +#endif /* USE_RFC3542 */ for (hops = 0; hops < argc - 1; hops++) { struct addrinfo *iaip; @@ -902,7 +902,7 @@ main(int argc, char *argv[]) errx(1, "bad addr family of an intermediate addr"); -#ifdef USE_RFC2292BIS +#ifdef USE_RFC3542 if (inet6_rth_add(rthdr, &(SIN6(iaip->ai_addr))->sin6_addr)) errx(1, "can't add an intermediate node"); @@ -911,11 +911,11 @@ main(int argc, char *argv[]) &(SIN6(iaip->ai_addr))->sin6_addr, IPV6_RTHDR_LOOSE)) errx(1, "can't add an intermediate node"); -#endif /* USE_RFC2292BIS */ +#endif /* USE_RFC3542 */ freeaddrinfo(iaip); } -#ifndef USE_RFC2292BIS +#ifndef USE_RFC3542 if (inet6_rthdr_lasthop(scmsgp, IPV6_RTHDR_LOOSE)) errx(1, "can't set the last flag"); #endif @@ -939,7 +939,7 @@ 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))) @@ -1728,7 +1728,7 @@ pr_exthdrs(struct msghdr *mhdr) } } -#ifdef USE_RFC2292BIS +#ifdef USE_RFC3542 void pr_ip6opt(void *extbuf) { @@ -1779,7 +1779,7 @@ pr_ip6opt(void *extbuf) } return; } -#else /* !USE_RFC2292BIS */ +#else /* !USE_RFC3542 */ /* ARGSUSED */ void pr_ip6opt(void *extbuf) @@ -1787,9 +1787,9 @@ pr_ip6opt(void *extbuf) putchar('\n'); return; } -#endif /* USE_RFC2292BIS */ +#endif /* USE_RFC3542 */ -#ifdef USE_RFC2292BIS +#ifdef USE_RFC3542 void pr_rthdr(void *extbuf) { @@ -1823,7 +1823,7 @@ pr_rthdr(void *extbuf) } -#else /* !USE_RFC2292BIS */ +#else /* !USE_RFC3542 */ /* ARGSUSED */ void pr_rthdr(void *extbuf) @@ -1831,7 +1831,7 @@ pr_rthdr(void *extbuf) putchar('\n'); return; } -#endif /* USE_RFC2292BIS */ +#endif /* USE_RFC3542 */ int pr_bitrange(u_int32_t v, int soff, int ii) Index: usr.bin/telnet/commands.c =================================================================== RCS file: /cvsroot/src/usr.bin/telnet/commands.c,v retrieving revision 1.64 diff -u -p -r1.64 commands.c --- usr.bin/telnet/commands.c 4 Apr 2006 21:35:20 -0000 1.64 +++ usr.bin/telnet/commands.c 29 Apr 2006 11:15:24 -0000 @@ -2799,6 +2799,8 @@ sourceroute(struct addrinfo *ai, char *a break; #ifdef INET6 case AF_INET6: +#ifdef IPV6_PKTOPTIONS + /* RFC2292 */ cmsg = inet6_rthdr_init(rhbuf, IPV6_RTHDR_TYPE_0); if (*cp != '@') return -1; @@ -2806,6 +2808,10 @@ sourceroute(struct addrinfo *ai, char *a *protop = IPPROTO_IPV6; *optp = IPV6_PKTOPTIONS; break; +#else + /* no RFC2292 */ + return -1; +#endif #endif default: return -1; Index: usr.sbin/traceroute6/Makefile =================================================================== RCS file: /cvsroot/src/usr.sbin/traceroute6/Makefile,v retrieving revision 1.5 diff -u -p -r1.5 Makefile --- usr.sbin/traceroute6/Makefile 8 Oct 2000 06:40:42 -0000 1.5 +++ usr.sbin/traceroute6/Makefile 29 Apr 2006 11:15:24 -0000 @@ -7,6 +7,7 @@ BINMODE=4555 BINOWN= root CPPFLAGS+=-DINET6 -DIPSEC +CPPFLAGS+=-DUSE_RFC3542 LDADD+= -lipsec DPADD+= ${LIBIPSEC} Index: usr.sbin/traceroute6/traceroute6.c =================================================================== RCS file: /cvsroot/src/usr.sbin/traceroute6/traceroute6.c,v retrieving revision 1.35 diff -u -p -r1.35 traceroute6.c --- usr.sbin/traceroute6/traceroute6.c 22 Apr 2004 01:41:22 -0000 1.35 +++ usr.sbin/traceroute6/traceroute6.c 29 Apr 2006 11:15:25 -0000 @@ -339,7 +339,7 @@ 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 +#ifdef USE_RFC3542 struct ip6_rthdr *rth; #endif struct cmsghdr *cmsg; @@ -436,7 +436,7 @@ main(argc, argv) "traceroute6: unknown host %s\n", optarg); exit(1); } -#ifdef USE_RFC2292BIS +#ifdef USE_RFC3542 if (rth == NULL) { /* * XXX: We can't detect the number of @@ -704,7 +704,7 @@ main(argc, argv) if (options & SO_DONTROUTE) (void) setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE, (char *)&on, sizeof(on)); -#ifdef USE_RFC2292BIS +#ifdef USE_RFC3542 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, @@ -724,7 +724,7 @@ main(argc, argv) exit(1); } } -#endif /* USE_RFC2292BIS */ +#endif /* USE_RFC3542 */ #ifdef IPSEC #ifdef IPSEC_POLICY_IPSEC /* Index: sys/netinet/ip6.h =================================================================== RCS file: /cvsroot/src/sys/netinet/ip6.h,v retrieving revision 1.20 diff -u -p -r1.20 ip6.h --- sys/netinet/ip6.h 10 Dec 2005 23:36:23 -0000 1.20 +++ sys/netinet/ip6.h 29 Apr 2006 11:15:26 -0000 @@ -154,7 +154,7 @@ struct ip6_dest { #define IP6OPT_NSAP_ADDR 0xC3 /* 11 0 00011 */ #define IP6OPT_TUNNEL_LIMIT 0x04 /* 00 0 00100 */ #define IP6OPT_RTALERT 0x05 /* 00 0 00101 (KAME definition) */ -#define IP6OPT_ROUTER_ALERT 0x05 /* (2292bis def, recommended) */ +#define IP6OPT_ROUTER_ALERT 0x05 /* (RFC3542 def, recommended) */ #define IP6OPT_RTALERT_LEN 4 #define IP6OPT_RTALERT_MLD 0 /* Datagram contains an MLD message */ Index: sys/netinet6/files.netinet6 =================================================================== RCS file: /cvsroot/src/sys/netinet6/files.netinet6,v retrieving revision 1.4 diff -u -p -r1.4 files.netinet6 --- sys/netinet6/files.netinet6 21 Jan 2006 00:15:36 -0000 1.4 +++ sys/netinet6/files.netinet6 29 Apr 2006 11:15:26 -0000 @@ -1,5 +1,7 @@ # $NetBSD: files.netinet6,v 1.4 2006/01/21 00:15:36 rpaulo Exp $ +defflag opt_inet6.h RFC2292 + # NOTE: netinet/files.netinet must be included before this one! file netinet6/dest6.c inet6 Index: sys/netinet6/in6.h =================================================================== RCS file: /cvsroot/src/sys/netinet6/in6.h,v retrieving revision 1.53 diff -u -p -r1.53 in6.h --- sys/netinet6/in6.h 29 Mar 2006 21:29:59 -0000 1.53 +++ sys/netinet6/in6.h 29 Apr 2006 11:15:26 -0000 @@ -68,6 +68,8 @@ #error "do not include netinet6/in6.h directly, include netinet/in.h. see RFC2553" #endif +#include + /* * Identification of the network protocol stack * for *BSD-current/release: http://www.kame.net/dev/cvsweb.cgi/kame/COVERAGE @@ -403,13 +405,16 @@ struct route_in6 { #if defined(_NETBSD_SOURCE) #define ICMP6_FILTER 18 /* icmp6_filter; icmp6 filter */ #endif -#define IPV6_PKTINFO 19 /* bool; send/rcv 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 */ +/* RFC2292 options */ +#ifdef _KERNEL +#define IPV6_2292PKTINFO 19 /* bool; send/recv if, src/dst addr */ +#define IPV6_2292HOPLIMIT 20 /* bool; hop limit */ +#define IPV6_2292NEXTHOP 21 /* bool; next hop addr */ +#define IPV6_2292HOPOPTS 22 /* bool; hop-by-hop option */ +#define IPV6_2292DSTOPTS 23 /* bool; destinaion option */ +#define IPV6_2292RTHDR 24 /* bool; routing header */ +#define IPV6_2292PKTOPTIONS 25 /* buf/cmsghdr; set/get IPv6 options */ +#endif #define IPV6_CHECKSUM 26 /* int; checksum offset for raw socket */ #define IPV6_V6ONLY 27 /* bool; make AF_INET6 sockets v6 only */ @@ -417,7 +422,38 @@ struct route_in6 { #define IPV6_IPSEC_POLICY 28 /* struct; get/set security policy */ #endif #define IPV6_FAITH 29 /* bool; accept FAITH'ed connections */ + +/* 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 */ +#define IPV6_PATHMTU 44 /* mtuinfo; get the current path MTU (sopt), + 4 bytes int; MTU notification (cmsg) */ + +/* more new socket options introduced in RFC3542 */ +#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_RECVTCLASS 57 /* bool; recv traffic class values */ +#ifdef _KERNEL +#define IPV6_OTCLASS 58 /* u_int8_t; send traffic class value */ +#endif + +#define IPV6_TCLASS 61 /* int; send traffic class value */ +#define IPV6_DONTFRAG 62 /* bool; disable IPv6 fragmentation */ /* 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 */ @@ -447,6 +483,14 @@ struct in6_pktinfo { }; /* + * Control structure for IPV6_RECVPATHMTU socket option. + */ +struct ip6_mtuinfo { + struct sockaddr_in6 ip6m_addr; /* or sockaddr_storage? */ + u_int32_t ip6m_mtu; +}; + +/* * Argument for IPV6_PORTRANGE: * - which range to search when port is unspecified at bind() or connect() */ @@ -708,6 +752,24 @@ extern int inet6_rthdr_reverse __P((cons extern int inet6_rthdr_segments __P((const struct cmsghdr *)); extern struct in6_addr *inet6_rthdr_getaddr __P((struct cmsghdr *, int)); extern int inet6_rthdr_getflags __P((const struct cmsghdr *, int)); + +extern int inet6_opt_init __P((void *, socklen_t)); +extern int inet6_opt_append __P((void *, socklen_t, int, u_int8_t, + socklen_t, u_int8_t, void **)); +extern int inet6_opt_finish __P((void *, socklen_t, int)); +extern int inet6_opt_set_val __P((void *, int, void *, socklen_t)); + +extern int inet6_opt_next __P((void *, socklen_t, int, u_int8_t *, + socklen_t *, void **)); +extern int inet6_opt_find __P((void *, socklen_t, int, u_int8_t, + socklen_t *, void **)); +extern int inet6_opt_get_val __P((void *, int, void *, socklen_t)); +extern socklen_t inet6_rth_space __P((int, int)); +extern void *inet6_rth_init __P((void *, socklen_t, int, int)); +extern int inet6_rth_add __P((void *, const struct in6_addr *)); +extern int inet6_rth_reverse __P((const void *, void *)); +extern int inet6_rth_segments __P((const void *)); +extern struct in6_addr *inet6_rth_getaddr __P((const void *, int)); __END_DECLS #endif /* _NETBSD_SOURCE */ Index: sys/netinet6/in6_pcb.c =================================================================== RCS file: /cvsroot/src/sys/netinet6/in6_pcb.c,v retrieving revision 1.69 diff -u -p -r1.69 in6_pcb.c --- sys/netinet6/in6_pcb.c 21 Jan 2006 00:15:36 -0000 1.69 +++ sys/netinet6/in6_pcb.c 29 Apr 2006 11:15:29 -0000 @@ -680,6 +680,22 @@ in6_pcbnotify(table, dst, fport_arg, src } /* + * 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 && (in6p->in6p_flags & IN6P_MTU) != 0 && + (IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr) || + IN6_ARE_ADDR_EQUAL(&in6p->in6p_faddr, &sa6_dst->sin6_addr))) { + ip6_notify_pmtu(in6p, (struct sockaddr_in6 *)dst, + (u_int32_t *)cmdarg); + } + + /* * Detect if we should notify the error. If no source and * destination ports are specified, but non-zero flowinfo and * local address match, notify the error. This is the case Index: sys/netinet6/in6_pcb.h =================================================================== RCS file: /cvsroot/src/sys/netinet6/in6_pcb.h,v retrieving revision 1.28 diff -u -p -r1.28 in6_pcb.h --- sys/netinet6/in6_pcb.h 26 Jan 2006 18:59:18 -0000 1.28 +++ sys/netinet6/in6_pcb.h 29 Apr 2006 11:15:29 -0000 @@ -123,19 +123,20 @@ struct in6pcb { #define IN6P_DSTOPTS 0x080000 /* receive dst options after rthdr */ #define IN6P_RTHDR 0x100000 /* receive routing header */ #define IN6P_RTHDRDSTOPTS 0x200000 /* receive dstoptions before rthdr */ +#define IN6P_TCLASS 0x400000 /* traffic class */ #define IN6P_HIGHPORT 0x1000000 /* user wants "high" port binding */ #define IN6P_LOWPORT 0x2000000 /* user wants "low" port binding */ #define IN6P_ANONPORT 0x4000000 /* port chosen for user */ #define IN6P_FAITH 0x8000000 /* accept FAITH'ed connections */ -#if 0 /* obsoleted */ -#define IN6P_BINDV6ONLY 0x10000000 /* do not grab IPv4 traffic */ -#endif -#define IN6P_MINMTU 0x20000000 /* use minimum MTU */ + +#define IN6P_RFC2292 0x40000000 /* RFC2292 */ +#define IN6P_MTU 0x80000000 /* use minimum MTU */ #define IN6P_CONTROLOPTS (IN6P_PKTINFO|IN6P_HOPLIMIT|IN6P_HOPOPTS|\ IN6P_DSTOPTS|IN6P_RTHDR|IN6P_RTHDRDSTOPTS|\ - IN6P_MINMTU) + IN6P_TCLASS|IN6P_RFC2292|\ + IN6P_MTU) /* compute hash value for foreign and local in6_addr and port */ #define IN6_HASH(faddr, fport, laddr, lport) \ Index: sys/netinet6/in6_src.c =================================================================== RCS file: /cvsroot/src/sys/netinet6/in6_src.c,v retrieving revision 1.24 diff -u -p -r1.24 in6_src.c --- sys/netinet6/in6_src.c 15 Apr 2006 00:30:48 -0000 1.24 +++ sys/netinet6/in6_src.c 29 Apr 2006 11:15:29 -0000 @@ -639,9 +639,7 @@ selectroute(dstsock, opts, mopts, ro, re * use it as the gateway. */ if (opts && opts->ip6po_nexthop) { -#ifdef notyet /* until introducing RFC3542 support */ struct route_in6 *ron; -#endif sin6_next = satosin6(opts->ip6po_nexthop); @@ -655,7 +653,6 @@ selectroute(dstsock, opts, mopts, ro, re * If the next hop is an IPv6 address, then the node identified * by that address must be a neighbor of the sending host. */ -#ifdef notyet /* see above */ ron = &opts->ip6po_nextroute; if ((ron->ro_rt && (ron->ro_rt->rt_flags & (RTF_UP | RTF_GATEWAY)) != @@ -696,7 +693,6 @@ selectroute(dstsock, opts, mopts, ro, re */ if (!clone) goto done; -#endif } /* Index: sys/netinet6/ip6_input.c =================================================================== RCS file: /cvsroot/src/sys/netinet6/ip6_input.c,v retrieving revision 1.84 diff -u -p -r1.84 ip6_input.c --- sys/netinet6/ip6_input.c 15 Apr 2006 00:13:23 -0000 1.84 +++ sys/netinet6/ip6_input.c 29 Apr 2006 11:15:31 -0000 @@ -65,6 +65,7 @@ __KERNEL_RCSID(0, "$NetBSD: ip6_input.c,v 1.84 2006/04/15 00:13:23 christos Exp $"); #include "opt_inet.h" +#include "opt_inet6.h" #include "opt_ipsec.h" #include "opt_pfil_hooks.h" @@ -1030,6 +1031,12 @@ ip6_savecontrol(in6p, mp, ip6, m) struct ip6_hdr *ip6; struct mbuf *m; { +#ifdef RFC2292 +#define IS2292(x, y) ((in6p->in6p_flags & IN6P_RFC2292) ? (x) : (y)) +#else +#define IS2292(x, y) (y) +#endif + #ifdef SO_TIMESTAMP if (in6p->in6p_socket->so_options & SO_TIMESTAMP) { struct timeval tv; @@ -1041,49 +1048,51 @@ ip6_savecontrol(in6p, mp, ip6, m) mp = &(*mp)->m_next; } #endif - if (in6p->in6p_flags & IN6P_RECVDSTADDR) { - *mp = sbcreatecontrol((caddr_t) &ip6->ip6_dst, - sizeof(struct in6_addr), IPV6_RECVDSTADDR, IPPROTO_IPV6); - if (*mp) - mp = &(*mp)->m_next; - } - -#ifdef noyet - /* options were tossed above */ - if (in6p->in6p_flags & IN6P_RECVOPTS) - /* broken */ - /* ip6_srcroute doesn't do what we want here, need to fix */ - if (in6p->in6p_flags & IPV6P_RECVRETOPTS) - /* broken */ -#endif - /* RFC 2292 sec. 5 */ if ((in6p->in6p_flags & IN6P_PKTINFO) != 0) { struct in6_pktinfo pi6; + bcopy(&ip6->ip6_dst, &pi6.ipi6_addr, sizeof(struct in6_addr)); in6_clearscope(&pi6.ipi6_addr); /* XXX */ pi6.ipi6_ifindex = m->m_pkthdr.rcvif ? m->m_pkthdr.rcvif->if_index : 0; *mp = sbcreatecontrol((caddr_t) &pi6, - sizeof(struct in6_pktinfo), IPV6_PKTINFO, IPPROTO_IPV6); + sizeof(struct in6_pktinfo), + IS2292(IPV6_2292PKTINFO, IPV6_PKTINFO), IPPROTO_IPV6); if (*mp) mp = &(*mp)->m_next; } + if (in6p->in6p_flags & IN6P_HOPLIMIT) { int hlim = ip6->ip6_hlim & 0xff; + *mp = sbcreatecontrol((caddr_t) &hlim, sizeof(int), - IPV6_HOPLIMIT, IPPROTO_IPV6); + 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; } - /* IN6P_NEXTHOP - for outgoing packet only */ /* * IPV6_HOPOPTS socket option. Recall that we required super-user * privilege for the option (see ip6_ctloutput), but it might be too * strict, since there might be some hop-by-hop options which can be * returned to normal user. - * See also RFC 2292 section 6. + * See also RFC3542 section 8 (or RFC2292 section 6). */ if ((in6p->in6p_flags & IN6P_HOPOPTS) != 0) { /* @@ -1117,10 +1126,11 @@ ip6_savecontrol(in6p, mp, ip6, m) * XXX: We copy whole the header even if a jumbo * payload option is included, which option is to * be removed before returning in the RFC 2292. - * But it's too painful operation... + * Note: this constraint is removed in RFC3542. */ *mp = sbcreatecontrol((caddr_t)hbh, hbhlen, - IPV6_HOPOPTS, IPPROTO_IPV6); + IS2292(IPV6_2292HOPOPTS, IPV6_HOPOPTS), + IPPROTO_IPV6); if (*mp) mp = &(*mp)->m_next; m_freem(ext); @@ -1181,7 +1191,8 @@ ip6_savecontrol(in6p, mp, ip6, m) break; *mp = sbcreatecontrol((caddr_t)ip6e, elen, - IPV6_DSTOPTS, IPPROTO_IPV6); + IS2292(IPV6_2292DSTOPTS, IPV6_DSTOPTS), + IPPROTO_IPV6); if (*mp) mp = &(*mp)->m_next; break; @@ -1191,7 +1202,8 @@ ip6_savecontrol(in6p, mp, ip6, m) break; *mp = sbcreatecontrol((caddr_t)ip6e, elen, - IPV6_RTHDR, IPPROTO_IPV6); + IS2292(IPV6_2292RTHDR, IPV6_RTHDR), + IPPROTO_IPV6); if (*mp) mp = &(*mp)->m_next; break; @@ -1223,6 +1235,45 @@ ip6_savecontrol(in6p, mp, ip6, m) ; } } +#undef IS2292 + + +void +ip6_notify_pmtu(struct in6pcb *in6p, struct sockaddr_in6 *dst, uint32_t *mtu) +{ + struct socket *so; + struct mbuf *m_mtu; + struct ip6_mtuinfo mtuctl; + + so = in6p->in6p_socket; + + if (mtu == NULL) + return; + +#ifdef DIAGNOSTIC + if (so == NULL) /* I believe this is impossible */ + panic("ip6_notify_pmtu: socket is NULL"); +#endif + + memset(&mtuctl, 0, sizeof(mtuctl)); /* zero-clear for safety */ + mtuctl.ip6m_mtu = *mtu; + mtuctl.ip6m_addr = *dst; + if (sa6_recoverscope(&mtuctl.ip6m_addr)) + return; + + 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; +} /* * pull single extension header from mbuf chain. returns single mbuf that @@ -1496,6 +1547,11 @@ u_char inet6ctlerrmap[PRC_NCMDS] = { SYSCTL_SETUP(sysctl_net_inet6_ip6_setup, "sysctl net.inet6.ip6 subtree setup") { +#ifdef RFC2292 +#define IS2292(x, y) ((in6p->in6p_flags & IN6P_RFC2292) ? (x) : (y)) +#else +#define IS2292(x, y) (y) +#endif sysctl_createv(clog, 0, NULL, NULL, CTLFLAG_PERMANENT, Index: sys/netinet6/ip6_output.c =================================================================== RCS file: /cvsroot/src/sys/netinet6/ip6_output.c,v retrieving revision 1.96 diff -u -p -r1.96 ip6_output.c --- sys/netinet6/ip6_output.c 15 Apr 2006 00:28:16 -0000 1.96 +++ sys/netinet6/ip6_output.c 29 Apr 2006 11:15:33 -0000 @@ -65,6 +65,7 @@ __KERNEL_RCSID(0, "$NetBSD: ip6_output.c,v 1.96 2006/04/15 00:28:16 christos Exp $"); #include "opt_inet.h" +#include "opt_inet6.h" #include "opt_ipsec.h" #include "opt_pfil_hooks.h" @@ -114,8 +115,11 @@ struct ip6_exthdrs { struct mbuf *ip6e_dest2; }; -static int ip6_pcbopts __P((struct ip6_pktopts **, struct mbuf *, - struct socket *)); +static int ip6_pcbopt __P((int, u_char *, int, struct ip6_pktopts **, + int, int)); +static int ip6_getpcbopt __P((struct ip6_pktopts *, int, struct mbuf **)); +static int ip6_setpktopt __P((int, u_char *, int, struct ip6_pktopts *, int, + int, int, int)); static int ip6_setmoptions __P((int, struct ip6_moptions **, struct mbuf *)); static int ip6_getmoptions __P((int, struct ip6_moptions *, struct mbuf **)); static int ip6_copyexthdr __P((struct mbuf **, caddr_t, int)); @@ -125,6 +129,12 @@ static int ip6_insert_jumboopt __P((stru static int ip6_splithdr __P((struct mbuf *, struct ip6_exthdrs *)); static int ip6_getpmtu __P((struct route_in6 *, struct route_in6 *, struct ifnet *, struct in6_addr *, u_long *, int *)); +static int copypktopts __P((struct ip6_pktopts *, struct ip6_pktopts *, int)); + +#ifdef RFC2292 +static int ip6_pcbopts __P((struct ip6_pktopts **, struct mbuf *, + struct socket *)); +#endif #define IN6_NEED_CHECKSUM(ifp, csum_flags) \ (__predict_true(((ifp)->if_flags & IFF_LOOPBACK) == 0 || \ @@ -509,7 +519,6 @@ skip_ipsec2:; ro = &opt->ip6po_route; dst = (struct sockaddr_in6 *)&ro->ro_dst; -#ifdef notyet /* this will be available with the RFC3542 support */ /* * if specified, try to fill in the traffic class field. * do not override if a non-zero value is already set. @@ -525,7 +534,6 @@ skip_ipsec2:; if (mask != 0) ip6->ip6_flow |= htonl((opt->ip6po_tclass & mask) << 20); } -#endif /* fill in or override the hop limit field, if necessary. */ if (opt && opt->ip6po_hlim != -1) @@ -672,7 +680,6 @@ skip_ipsec2:; routefound: if (rt && !IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { -#ifdef notyet /* this will be available with the RFC3542 support */ if (opt && opt->ip6po_nextroute.ro_rt) { /* * The nexthop is explicitly specified by the @@ -680,10 +687,8 @@ skip_ipsec2:; * address. */ dst = (struct sockaddr_in6 *)opt->ip6po_nexthop; - } else -#endif - if ((rt->rt_flags & RTF_GATEWAY)) - dst = (struct sockaddr_in6 *)rt->rt_gateway; + } else if ((rt->rt_flags & RTF_GATEWAY)) + dst = (struct sockaddr_in6 *)rt->rt_gateway; } /* @@ -771,10 +776,25 @@ skip_ipsec2:; /* * 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 (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; + } } /* @@ -844,8 +864,11 @@ skip_ipsec2:; */ tlen = m->m_pkthdr.len; - dontfrag = 0; -#ifdef notdef + if (opt && (opt->ip6po_flags & IP6PO_DONTFRAG)) + dontfrag = 1; + else + dontfrag = 0; + if (dontfrag && alwaysfrag) { /* case 4 */ /* conflicting request - can't transmit */ error = EMSGSIZE; @@ -872,7 +895,7 @@ skip_ipsec2:; error = EMSGSIZE; goto bad; } -#endif + /* * transmit packet without fragmentation */ @@ -900,7 +923,7 @@ skip_ipsec2:; m->m_pkthdr.csum_flags &= ~(M_CSUM_UDPv6|M_CSUM_TCPv6); } - error = nd6_output(ifp, origifp, m, dst, ro->ro_rt); + error = nd6_output(ifp, origifp, m, dst, rt); goto done; } @@ -1059,7 +1082,7 @@ sendorfree: /* clean ipsec history once it goes out of the node */ ipsec_delaux(m); #endif - error = nd6_output(ifp, origifp, m, dst, ro->ro_rt); + error = nd6_output(ifp, origifp, m, dst, rt); } else m_freem(m); } @@ -1377,38 +1400,63 @@ ip6_ctloutput(op, so, level, optname, mp int level, optname; struct mbuf **mp; { + int privileged, optdatalen, uproto; + void *optdata; struct in6pcb *in6p = sotoin6pcb(so); struct mbuf *m = *mp; - int optval = 0; - int error = 0; + int error, optval; + int optlen; struct proc *p = curproc; /* XXX */ + optlen = m ? m->m_len : 0; + error = optval = 0; + privileged = (p == 0 || suser(p->p_ucred, &p->p_acflag)) ? 0 : 1; + uproto = (int)so->so_proto->pr_protocol; + if (level == IPPROTO_IPV6) { switch (op) { case PRCO_SETOPT: switch (optname) { - case IPV6_PKTOPTIONS: +#ifdef RFC2292 + case IPV6_2292PKTOPTIONS: /* m is freed in ip6_pcbopts */ - return (ip6_pcbopts(&in6p->in6p_outputopts, - m, so)); - case IPV6_HOPOPTS: - case IPV6_DSTOPTS: - if (p == 0 || suser(p->p_ucred, &p->p_acflag)) { + error = ip6_pcbopts(&in6p->in6p_outputopts, + m, so); + break; +#endif + + /* + * Use of some Hop-by-Hop options or some + * Destination options, might require special + * privilege. That is, normal applications + * (without special privilege) might be forbidden + * from setting certain options in outgoing packets, + * and might never see certain options in received + * packets. [RFC 2292 Section 6] + * KAME specific note: + * KAME prevents non-privileged users from sending or + * receiving ANY hbh/dst options in order to avoid + * overhead of parsing options in the kernel. + */ + case IPV6_RECVHOPOPTS: + case IPV6_RECVDSTOPTS: + case IPV6_RECVRTHDRDSTOPTS: + if (!privileged) { error = EPERM; break; } /* FALLTHROUGH */ case IPV6_UNICAST_HOPS: - case IPV6_RECVOPTS: - case IPV6_RECVRETOPTS: - case IPV6_RECVDSTADDR: - case IPV6_PKTINFO: case IPV6_HOPLIMIT: - case IPV6_RTHDR: case IPV6_FAITH: + + case IPV6_RECVPKTINFO: + case IPV6_RECVHOPLIMIT: + case IPV6_RECVRTHDR: + case IPV6_RECVPATHMTU: + case IPV6_RECVTCLASS: case IPV6_V6ONLY: - case IPV6_USE_MIN_MTU: - if (!m || m->m_len != sizeof(int)) { + if (optlen != sizeof(int)) { error = EINVAL; break; } @@ -1431,35 +1479,102 @@ do { \ in6p->in6p_flags &= ~(bit); \ } while (/*CONSTCOND*/ 0) - case IPV6_RECVOPTS: - OPTSET(IN6P_RECVOPTS); - break; - - case IPV6_RECVRETOPTS: - OPTSET(IN6P_RECVRETOPTS); - break; +#ifdef RFC2292 +#define OPTSET2292(bit) \ +do { \ + in6p->in6p_flags |= IN6P_RFC2292; \ + if (optval) \ + in6p->in6p_flags |= (bit); \ + else \ + in6p->in6p_flags &= ~(bit); \ +} while (/*CONSTCOND*/ 0) +#endif - case IPV6_RECVDSTADDR: - OPTSET(IN6P_RECVDSTADDR); - break; +#define OPTBIT(bit) (in6p->in6p_flags & (bit) ? 1 : 0) - case IPV6_PKTINFO: + case IPV6_RECVPKTINFO: +#ifdef RFC2292 + /* cannot mix with RFC2292 */ + if (OPTBIT(IN6P_RFC2292)) { + error = EINVAL; + break; + } +#endif OPTSET(IN6P_PKTINFO); break; case IPV6_HOPLIMIT: + { + struct ip6_pktopts **optp; + +#ifdef RFC2292 + /* cannot mix with RFC2292 */ + if (OPTBIT(IN6P_RFC2292)) { + error = EINVAL; + break; + } +#endif + optp = &in6p->in6p_outputopts; + error = ip6_pcbopt(IPV6_HOPLIMIT, + (u_char *)&optval, + sizeof(optval), + optp, + privileged, uproto); + break; + } + + case IPV6_RECVHOPLIMIT: +#ifdef RFC2292 + /* cannot mix with RFC2292 */ + if (OPTBIT(IN6P_RFC2292)) { + error = EINVAL; + break; + } +#endif OPTSET(IN6P_HOPLIMIT); break; - case IPV6_HOPOPTS: + case IPV6_RECVHOPOPTS: +#ifdef RFC2292 + /* cannot mix with RFC2292 */ + if (OPTBIT(IN6P_RFC2292)) { + error = EINVAL; + break; + } +#endif OPTSET(IN6P_HOPOPTS); break; - case IPV6_DSTOPTS: + case IPV6_RECVDSTOPTS: +#ifdef RFC2292 + /* cannot mix with RFC2292 */ + if (OPTBIT(IN6P_RFC2292)) { + error = EINVAL; + break; + } +#endif OPTSET(IN6P_DSTOPTS); break; - case IPV6_RTHDR: + case IPV6_RECVRTHDRDSTOPTS: +#ifdef RFC2292 + /* cannot mix with RFC2292 */ + if (OPTBIT(IN6P_RFC2292)) { + error = EINVAL; + break; + } +#endif + OPTSET(IN6P_RTHDRDSTOPTS); + break; + + case IPV6_RECVRTHDR: +#ifdef RFC2292 + /* cannot mix with RFC2292 */ + if (OPTBIT(IN6P_RFC2292)) { + error = EINVAL; + break; + } +#endif OPTSET(IN6P_RTHDR); break; @@ -1467,8 +1582,15 @@ do { \ OPTSET(IN6P_FAITH); break; - case IPV6_USE_MIN_MTU: - OPTSET(IN6P_MINMTU); + 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_V6ONLY: @@ -1489,8 +1611,134 @@ do { \ OPTSET(IN6P_IPV6_V6ONLY); #endif break; + case IPV6_RECVTCLASS: +#ifdef RFC2292 + /* cannot mix with RFC2292 XXX */ + if (OPTBIT(IN6P_RFC2292)) { + error = EINVAL; + break; + } +#endif + OPTSET(IN6P_TCLASS); + break; + + } + break; + + case IPV6_OTCLASS: + { + struct ip6_pktopts **optp; + u_int8_t tclass; + + if (optlen != sizeof(tclass)) { + error = EINVAL; + break; + } + tclass = *mtod(m, u_int8_t *); + optp = &in6p->in6p_outputopts; + error = ip6_pcbopt(optname, + (u_char *)&tclass, + sizeof(tclass), + optp, + privileged, uproto); + break; + } + + case IPV6_TCLASS: + case IPV6_DONTFRAG: + case IPV6_USE_MIN_MTU: + if (optlen != sizeof(optval)) { + error = EINVAL; + break; + } + optval = *mtod(m, int *); + { + struct ip6_pktopts **optp; + optp = &in6p->in6p_outputopts; + error = ip6_pcbopt(optname, + (u_char *)&optval, + sizeof(optval), + optp, + privileged, uproto); + break; + } + +#ifdef RFC2292 + case IPV6_2292PKTINFO: + case IPV6_2292HOPLIMIT: + case IPV6_2292HOPOPTS: + case IPV6_2292DSTOPTS: + case IPV6_2292RTHDR: + /* RFC 2292 */ + if (optlen != sizeof(int)) { + error = EINVAL; + break; + } + optval = *mtod(m, int *); + switch (optname) { + case IPV6_2292PKTINFO: + OPTSET2292(IN6P_PKTINFO); + break; + case IPV6_2292HOPLIMIT: + OPTSET2292(IN6P_HOPLIMIT); + break; + case IPV6_2292HOPOPTS: + /* + * Check super-user privilege. + * See comments for IPV6_RECVHOPOPTS. + */ + if (!privileged) + return (EPERM); + OPTSET2292(IN6P_HOPOPTS); + break; + case IPV6_2292DSTOPTS: + if (!privileged) + return (EPERM); + OPTSET2292(IN6P_DSTOPTS|IN6P_RTHDRDSTOPTS); /* XXX */ + break; + case IPV6_2292RTHDR: + OPTSET2292(IN6P_RTHDR); + break; + } + break; +#endif + 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; + int optbuflen; + struct ip6_pktopts **optp; + +#ifdef RFC2292 + /* cannot mix with RFC2292 */ + if (OPTBIT(IN6P_RFC2292)) { + error = EINVAL; + break; + } +#endif + + if (m && m->m_next) { + error = EINVAL; /* XXX */ + break; + } + if (m) { + optbuf = mtod(m, u_char *); + optbuflen = m->m_len; + } else { + optbuf = NULL; + optbuflen = 0; } + optp = &in6p->in6p_outputopts; + error = ip6_pcbopt(optname, + optbuf, optbuflen, + optp, privileged, uproto); break; + } #undef OPTSET case IPV6_MULTICAST_IF: @@ -1498,7 +1746,7 @@ do { \ case IPV6_MULTICAST_LOOP: case IPV6_JOIN_GROUP: case IPV6_LEAVE_GROUP: - error = ip6_setmoptions(optname, + error = ip6_setmoptions(optname, &in6p->in6p_moptions, m); break; @@ -1529,22 +1777,16 @@ do { \ #ifdef IPSEC case IPV6_IPSEC_POLICY: - { + { caddr_t req = NULL; size_t len = 0; - - int priv = 0; - if (p == 0 || suser(p->p_ucred, &p->p_acflag)) - priv = 0; - else - priv = 1; if (m) { req = mtod(m, caddr_t); len = m->m_len; } - error = ipsec6_set_policy(in6p, - optname, req, len, priv); - } + error = ipsec6_set_policy(in6p, optname, req, + len, privileged); + } break; #endif /* IPSEC */ @@ -1558,60 +1800,75 @@ do { \ case PRCO_GETOPT: switch (optname) { - - case IPV6_OPTIONS: - case IPV6_RETOPTS: - error = ENOPROTOOPT; - break; - - case IPV6_PKTOPTIONS: - if (in6p->in6p_options) { - *mp = m_copym(in6p->in6p_options, 0, - M_COPYALL, M_WAIT); - } else { - *mp = m_get(M_WAIT, MT_SOOPTS); - (*mp)->m_len = 0; - } +#ifdef RFC2292 + case IPV6_2292PKTOPTIONS: + /* + * 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. + */ + *mp = m_get(M_WAIT, MT_SOOPTS); + (*mp)->m_len = 0; break; +#endif - case IPV6_HOPOPTS: - case IPV6_DSTOPTS: - if (p == 0 || suser(p->p_ucred, &p->p_acflag)) { - error = EPERM; - break; - } - /* FALLTHROUGH */ + case IPV6_RECVHOPOPTS: + case IPV6_RECVDSTOPTS: + case IPV6_RECVRTHDRDSTOPTS: case IPV6_UNICAST_HOPS: - case IPV6_RECVOPTS: - case IPV6_RECVRETOPTS: - case IPV6_RECVDSTADDR: - case IPV6_PORTRANGE: - case IPV6_PKTINFO: - case IPV6_HOPLIMIT: - case IPV6_RTHDR: + case IPV6_RECVPKTINFO: + case IPV6_RECVHOPLIMIT: + case IPV6_RECVRTHDR: + case IPV6_RECVPATHMTU: + case IPV6_FAITH: case IPV6_V6ONLY: - case IPV6_USE_MIN_MTU: - *mp = m = m_get(M_WAIT, MT_SOOPTS); - m->m_len = sizeof(int); + case IPV6_PORTRANGE: + case IPV6_RECVTCLASS: switch (optname) { + case IPV6_RECVHOPOPTS: + optval = OPTBIT(IN6P_HOPOPTS); + break; + + case IPV6_RECVDSTOPTS: + optval = OPTBIT(IN6P_DSTOPTS); + break; + + case IPV6_RECVRTHDRDSTOPTS: + optval = OPTBIT(IN6P_RTHDRDSTOPTS); + break; + case IPV6_UNICAST_HOPS: optval = in6p->in6p_hops; break; -#define OPTBIT(bit) (in6p->in6p_flags & bit ? 1 : 0) + case IPV6_RECVPKTINFO: + optval = OPTBIT(IN6P_PKTINFO); + break; - case IPV6_RECVOPTS: - optval = OPTBIT(IN6P_RECVOPTS); + case IPV6_RECVHOPLIMIT: + optval = OPTBIT(IN6P_HOPLIMIT); break; - case IPV6_RECVRETOPTS: - optval = OPTBIT(IN6P_RECVRETOPTS); + case IPV6_RECVRTHDR: + optval = OPTBIT(IN6P_RTHDR); break; - case IPV6_RECVDSTADDR: - optval = OPTBIT(IN6P_RECVDSTADDR); + case IPV6_RECVPATHMTU: + optval = OPTBIT(IN6P_MTU); + break; + + case IPV6_FAITH: + optval = OPTBIT(IN6P_FAITH); + break; + + case IPV6_V6ONLY: + optval = OPTBIT(IN6P_IPV6_V6ONLY); break; case IPV6_PORTRANGE: @@ -1626,66 +1883,121 @@ do { \ optval = 0; break; } - - case IPV6_PKTINFO: - optval = OPTBIT(IN6P_PKTINFO); + case IPV6_RECVTCLASS: + optval = OPTBIT(IN6P_TCLASS); break; - case IPV6_HOPLIMIT: - optval = OPTBIT(IN6P_HOPLIMIT); + } + if (error) break; + *mp = m = m_get(M_WAIT, MT_SOOPTS); + m->m_len = sizeof(int); + *mtod(m, int *) = optval; + break; - case IPV6_HOPOPTS: - optval = OPTBIT(IN6P_HOPOPTS); - break; + case IPV6_PATHMTU: + { + u_long pmtu = 0; + struct ip6_mtuinfo mtuinfo; + struct route_in6 *ro = (struct route_in6 *)&in6p +->in6p_route; - case IPV6_DSTOPTS: - optval = OPTBIT(IN6P_DSTOPTS); - break; + if (!(so->so_state & SS_ISCONNECTED)) + return (ENOTCONN); + /* + * XXX: we dot not consider the case of source + * routing, or optional information to specify + * the outgoing interface. + */ + error = ip6_getpmtu(ro, NULL, NULL, + &in6p->in6p_faddr, &pmtu, NULL); + if (error) + break; + if (pmtu > IPV6_MAXPACKET) + pmtu = IPV6_MAXPACKET; + + memset(&mtuinfo, 0, sizeof(mtuinfo)); + mtuinfo.ip6m_mtu = (u_int32_t)pmtu; + optdata = (void *)&mtuinfo; + optdatalen = sizeof(mtuinfo); + if (optdatalen > MCLBYTES) + return (EMSGSIZE); /* XXX */ + *mp = m = m_get(M_WAIT, MT_SOOPTS); + if (optdatalen > MLEN) + MCLGET(m, M_WAIT); + m->m_len = optdatalen; + memcpy(mtod(m, void *), optdata, optdatalen); + break; + } - case IPV6_RTHDR: - optval = OPTBIT(IN6P_RTHDR); +#ifdef RFC2292 + case IPV6_2292PKTINFO: + case IPV6_2292HOPLIMIT: + case IPV6_2292HOPOPTS: + case IPV6_2292RTHDR: + case IPV6_2292DSTOPTS: + switch (optname) { + case IPV6_2292PKTINFO: + optval = OPTBIT(IN6P_PKTINFO); break; - - case IPV6_FAITH: - optval = OPTBIT(IN6P_FAITH); + case IPV6_2292HOPLIMIT: + optval = OPTBIT(IN6P_HOPLIMIT); break; - - case IPV6_V6ONLY: - optval = OPTBIT(IN6P_IPV6_V6ONLY); + case IPV6_2292HOPOPTS: + optval = OPTBIT(IN6P_HOPOPTS); break; - - case IPV6_USE_MIN_MTU: - optval = OPTBIT(IN6P_MINMTU); + case IPV6_2292RTHDR: + optval = OPTBIT(IN6P_RTHDR); + break; + case IPV6_2292DSTOPTS: + optval = OPTBIT(IN6P_DSTOPTS|IN6P_RTHDRDSTOPTS); break; } + *mp = m = m_get(M_WAIT, MT_SOOPTS); + m->m_len = sizeof(int); *mtod(m, int *) = optval; break; +#endif + case IPV6_PKTINFO: + case IPV6_HOPOPTS: + case IPV6_RTHDR: + case IPV6_DSTOPTS: + case IPV6_RTHDRDSTOPTS: + case IPV6_NEXTHOP: + case IPV6_OTCLASS: + case IPV6_TCLASS: + case IPV6_DONTFRAG: + case IPV6_USE_MIN_MTU: + error = ip6_getpcbopt(in6p->in6p_outputopts, + optname, mp); + break; case IPV6_MULTICAST_IF: case IPV6_MULTICAST_HOPS: case IPV6_MULTICAST_LOOP: case IPV6_JOIN_GROUP: case IPV6_LEAVE_GROUP: - error = ip6_getmoptions(optname, in6p->in6p_moptions, mp); + error = ip6_getmoptions(optname, + in6p->in6p_moptions, mp); break; -#if 0 /* defined(IPSEC) */ - /* XXX: code broken */ +#ifdef IPSEC case IPV6_IPSEC_POLICY: - { + { caddr_t req = NULL; size_t len = 0; - if (m) { req = mtod(m, caddr_t); len = m->m_len; } error = ipsec6_get_policy(in6p, req, len, mp); break; - } + } #endif /* IPSEC */ + + + default: error = ENOPROTOOPT; break; @@ -1726,9 +2038,9 @@ ip6_raw_ctloutput(op, so, level, optname * For ICMPv6 sockets, no modification allowed for checksum * offset, permit "no change" values to help existing apps. * - * XXX 2292bis says: "An attempt to set IPV6_CHECKSUM - * for an ICMPv6 socket will fail." - * The current behavior does not meet 2292bis. + * XXX RFC3542 says: "An attempt to set IPV6_CHECKSUM + * for an ICMPv6 socket will fail." The current + * behavior does not meet RFC3542. */ switch (op) { case PRCO_SETOPT: @@ -1776,10 +2088,10 @@ ip6_raw_ctloutput(op, so, level, optname return (error); } +#ifdef RFC2292 /* - * Set up IP6 options in pcb for insertion in output packets. - * Store in mbuf with pointer in pcbopt, adding pseudo-option - * with destination address if source routed. + * Set up IP6 options in pcb for insertion in output packets or + * specifying behavior of outgoing packets. */ static int ip6_pcbopts(pktopt, m, so) @@ -1794,33 +2106,298 @@ ip6_pcbopts(pktopt, m, so) /* turn off any old options. */ if (opt) { - if (opt->ip6po_m) - (void)m_free(opt->ip6po_m); +#ifdef DIAGNOSTIC + if (opt->ip6po_pktinfo || opt->ip6po_nexthop || + opt->ip6po_hbh || opt->ip6po_dest1 || opt->ip6po_dest2 || + opt->ip6po_rhinfo.ip6po_rhi_rthdr) + printf("ip6_pcbopts: all specified options are cleared.\n"); +#endif + ip6_clearpktopts(opt, -1); } else opt = malloc(sizeof(*opt), M_IP6OPT, M_WAITOK); - *pktopt = 0; + *pktopt = NULL; if (!m || m->m_len == 0) { /* - * Only turning off any previous options. + * Only turning off any previous options, regardless of + * whether the opt is just created or given. */ free(opt, M_IP6OPT); - if (m) - (void)m_free(m); return (0); } /* set options specified by user. */ if (p && !suser(p->p_ucred, &p->p_acflag)) priv = 1; - if ((error = ip6_setpktoptions(m, opt, priv)) != 0) { - (void)m_free(m); + if ((error = ip6_setpktopts(m, opt, NULL, priv, + so->so_proto->pr_protocol)) != 0) { + ip6_clearpktopts(opt, -1); /* XXX: discard all options */ free(opt, M_IP6OPT); return (error); } *pktopt = opt; return (0); } +#endif + +/* + * initialize ip6_pktopts. beware that there are non-zero default values in + * the struct. + */ +void +ip6_initpktopts(struct ip6_pktopts *opt) +{ + + memset(opt, 0, 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; +} + +#define sin6tosa(sin6) ((struct sockaddr *)(sin6)) /* XXX */ +static int +ip6_pcbopt(int optname, u_char *buf, int len, struct ip6_pktopts **pktopt, + int priv, int uproto) +{ + struct ip6_pktopts *opt; + + if (*pktopt == NULL) { + *pktopt = malloc(sizeof(struct ip6_pktopts), M_IP6OPT, + M_WAITOK); + ip6_initpktopts(*pktopt); + } + opt = *pktopt; + + return (ip6_setpktopt(optname, buf, len, opt, priv, 1, 0, uproto)); +} + +static int +ip6_getpcbopt(struct ip6_pktopts *pktopt, int optname, struct mbuf **mp) +{ + 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; + struct mbuf *m; + + 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... */ + memset(&null_pktinfo, 0, sizeof(null_pktinfo)); + optdata = (void *)&null_pktinfo; + } + optdatalen = sizeof(struct in6_pktinfo); + break; + case IPV6_OTCLASS: + /* XXX */ + return (EINVAL); + 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; + default: /* should not happen */ +#ifdef DIAGNOSTIC + panic("ip6_getpcbopt: unexpected option\n"); +#endif + return (ENOPROTOOPT); + } + + if (optdatalen > MCLBYTES) + return (EMSGSIZE); /* XXX */ + *mp = m = m_get(M_WAIT, MT_SOOPTS); + if (optdatalen > MLEN) + MCLGET(m, M_WAIT); + m->m_len = optdatalen; + if (optdatalen) + memcpy(mtod(m, void *), optdata, optdatalen); + + return (error); +} + +void +ip6_clearpktopts(struct ip6_pktopts *pktopt, int optname) +{ + if (optname == -1 || optname == IPV6_PKTINFO) { + if (pktopt->ip6po_pktinfo) + free(pktopt->ip6po_pktinfo, M_IP6OPT); + pktopt->ip6po_pktinfo = NULL; + } + if (optname == -1 || optname == IPV6_HOPLIMIT) + pktopt->ip6po_hlim = -1; + if (optname == -1 || optname == IPV6_TCLASS) + pktopt->ip6po_tclass = -1; + if (optname == -1 || optname == IPV6_NEXTHOP) { + if (pktopt->ip6po_nextroute.ro_rt) { + RTFREE(pktopt->ip6po_nextroute.ro_rt); + pktopt->ip6po_nextroute.ro_rt = NULL; + } + if (pktopt->ip6po_nexthop) + free(pktopt->ip6po_nexthop, M_IP6OPT); + pktopt->ip6po_nexthop = NULL; + } + if (optname == -1 || optname == IPV6_HOPOPTS) { + if (pktopt->ip6po_hbh) + free(pktopt->ip6po_hbh, M_IP6OPT); + pktopt->ip6po_hbh = NULL; + } + if (optname == -1 || optname == IPV6_RTHDRDSTOPTS) { + if (pktopt->ip6po_dest1) + free(pktopt->ip6po_dest1, M_IP6OPT); + pktopt->ip6po_dest1 = NULL; + } + if (optname == -1 || optname == IPV6_RTHDR) { + if (pktopt->ip6po_rhinfo.ip6po_rhi_rthdr) + free(pktopt->ip6po_rhinfo.ip6po_rhi_rthdr, M_IP6OPT); + pktopt->ip6po_rhinfo.ip6po_rhi_rthdr = NULL; + if (pktopt->ip6po_route.ro_rt) { + RTFREE(pktopt->ip6po_route.ro_rt); + pktopt->ip6po_route.ro_rt = NULL; + } + } + if (optname == -1 || optname == IPV6_DSTOPTS) { + if (pktopt->ip6po_dest2) + free(pktopt->ip6po_dest2, M_IP6OPT); + pktopt->ip6po_dest2 = NULL; + } +} + +#define PKTOPT_EXTHDRCPY(type) \ +do { \ + if (src->type) { \ + int hlen = (((struct ip6_ext *)src->type)->ip6e_len + 1) << 3;\ + dst->type = malloc(hlen, M_IP6OPT, canwait); \ + if (dst->type == NULL && canwait == M_NOWAIT) \ + goto bad; \ + memcpy(dst->type, src->type, hlen); \ + } \ +} while (/*CONSTCOND*/ 0) + +static int +copypktopts(struct ip6_pktopts *dst, struct ip6_pktopts *src, int canwait) +{ + dst->ip6po_hlim = src->ip6po_hlim; + dst->ip6po_tclass = src->ip6po_tclass; + dst->ip6po_flags = src->ip6po_flags; + if (src->ip6po_pktinfo) { + dst->ip6po_pktinfo = malloc(sizeof(*dst->ip6po_pktinfo), + M_IP6OPT, canwait); + if (dst->ip6po_pktinfo == NULL && canwait == M_NOWAIT) + goto bad; + *dst->ip6po_pktinfo = *src->ip6po_pktinfo; + } + if (src->ip6po_nexthop) { + dst->ip6po_nexthop = malloc(src->ip6po_nexthop->sa_len, + M_IP6OPT, canwait); + if (dst->ip6po_nexthop == NULL && canwait == M_NOWAIT) + goto bad; + memcpy(dst->ip6po_nexthop, src->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: + if (dst->ip6po_pktinfo) free(dst->ip6po_pktinfo, M_IP6OPT); + if (dst->ip6po_nexthop) free(dst->ip6po_nexthop, M_IP6OPT); + if (dst->ip6po_hbh) free(dst->ip6po_hbh, M_IP6OPT); + if (dst->ip6po_dest1) free(dst->ip6po_dest1, M_IP6OPT); + if (dst->ip6po_dest2) free(dst->ip6po_dest2, M_IP6OPT); + if (dst->ip6po_rthdr) free(dst->ip6po_rthdr, M_IP6OPT); + + return (ENOBUFS); +} +#undef PKTOPT_EXTHDRCPY + +struct ip6_pktopts * +ip6_copypktopts(struct ip6_pktopts *src, int canwait) +{ + int error; + struct ip6_pktopts *dst; + + dst = malloc(sizeof(*dst), M_IP6OPT, canwait); + if (dst == NULL && canwait == M_NOWAIT) + return (NULL); + ip6_initpktopts(dst); + + if ((error = copypktopts(dst, src, canwait)) != 0) { + free(dst, M_IP6OPT); + return (NULL); + } + + return (dst); +} + +void +ip6_freepcbopts(struct ip6_pktopts *pktopt) +{ + if (pktopt == NULL) + return; + + ip6_clearpktopts(pktopt, -1); + + free(pktopt, M_IP6OPT); +} /* * Set the IP6 multicast options in response to user setsockopt(). @@ -2200,18 +2777,32 @@ ip6_freemoptions(im6o) * Set IPv6 outgoing packet options based on advanced API. */ int -ip6_setpktoptions(control, opt, priv) +ip6_setpktopts(control, opt, stickyopt, priv, uproto) struct mbuf *control; - struct ip6_pktopts *opt; - int priv; + struct ip6_pktopts *opt, *stickyopt; + int priv, uproto; { struct cmsghdr *cm = 0; - if (control == 0 || opt == 0) + if (control == NULL || opt == NULL) return (EINVAL); - bzero(opt, sizeof(*opt)); - opt->ip6po_hlim = -1; /* -1 means to use default hop limit */ + ip6_initpktopts(opt); + 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 @@ -2220,174 +2811,415 @@ ip6_setpktoptions(control, opt, priv) if (control->m_next) return (EINVAL); - opt->ip6po_m = control; - 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; - switch (cm->cmsg_type) { - case IPV6_PKTINFO: - if (cm->cmsg_len != CMSG_LEN(sizeof(struct in6_pktinfo))) - return (EINVAL); - opt->ip6po_pktinfo = (struct in6_pktinfo *)CMSG_DATA(cm); - if (opt->ip6po_pktinfo->ipi6_ifindex >= if_indexlim || - opt->ip6po_pktinfo->ipi6_ifindex < 0) - return (ENXIO); - if (opt->ip6po_pktinfo->ipi6_ifindex > 0 && - !ifindex2ifnet[opt->ip6po_pktinfo->ipi6_ifindex]) - return (ENXIO); + error = ip6_setpktopt(cm->cmsg_type, CMSG_DATA(cm), + cm->cmsg_len - CMSG_LEN(0), opt, priv, 0, 1, uproto); + if (error) + return (error); + } - if (opt->ip6po_pktinfo->ipi6_ifindex) { - struct ifnet *ifp; - int error; - - /* ipi6_ifindex must be valid here */ - ifp = ifindex2ifnet[opt->ip6po_pktinfo->ipi6_ifindex]; - error = in6_setscope(&opt->ip6po_pktinfo->ipi6_addr, - ifp, NULL); - if (error != 0) - return (error); - } + return (0); +} - /* - * 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 ifaddr *ia; - 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; - ia = ifa_ifwithaddr(sin6tosa(&sin6)); - if (ia == NULL || - (opt->ip6po_pktinfo->ipi6_ifindex && - (ia->ifa_ifp->if_index != - opt->ip6po_pktinfo->ipi6_ifindex))) { - return (EADDRNOTAVAIL); - } - ia6 = (struct in6_ifaddr *)ia; - if ((ia6->ia6_flags & (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY)) != 0) { - return (EADDRNOTAVAIL); - } +/* + * 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 + */ +static int +ip6_setpktopt(int optname, u_char *buf, int len, struct ip6_pktopts *opt, + int priv, int sticky, int cmsg, int uproto) +{ + int minmtupolicy; - /* - * Check if the requested source address is - * indeed a unicast address assigned to the - * node. - */ - if (IN6_IS_ADDR_MULTICAST(&opt->ip6po_pktinfo->ipi6_addr)) - return (EADDRNOTAVAIL); - } - break; + if (!sticky && !cmsg) { +#ifdef DIAGNOSTIC + printf("ip6_setpktopt: impossible case\n"); +#endif + return (EINVAL); + } + /* + * 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 (!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: - if (cm->cmsg_len != CMSG_LEN(sizeof(int))) - return (EINVAL); - else { - int t; + 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_OTCLASS: + case IPV6_TCLASS: + return (ENOPROTOOPT); + } + } - bcopy(CMSG_DATA(cm), &t, sizeof(t)); - if (t < -1 || t > 255) - return (EINVAL); - opt->ip6po_hlim = t; - } + switch (optname) { +#ifdef RFC2292 + case IPV6_2292PKTINFO: +#endif + case IPV6_PKTINFO: + { + struct ifnet *ifp = NULL; + struct in6_pktinfo *pktinfo; + + if (len != sizeof(struct in6_pktinfo)) + return (EINVAL); + + pktinfo = (struct in6_pktinfo *)buf; + + /* + * An application can clear any sticky IPV6_PKTINFO option by + * doing a "regular" setsockopt with ipi6_addr being + * in6addr_any and ipi6_ifindex being zero. + * [RFC 3542, Section 6] + */ + if (optname == IPV6_PKTINFO && opt->ip6po_pktinfo && + pktinfo->ipi6_ifindex == 0 && + IN6_IS_ADDR_UNSPECIFIED(&pktinfo->ipi6_addr)) { + ip6_clearpktopts(opt, optname); break; + } - case IPV6_NEXTHOP: - if (!priv) - return (EPERM); + if (uproto == IPPROTO_TCP && optname == IPV6_PKTINFO && + sticky && !IN6_IS_ADDR_UNSPECIFIED(&pktinfo->ipi6_addr)) { + return (EINVAL); + } - /* check if cmsg_len is large enough for sa_len */ - if (cm->cmsg_len < sizeof(u_char) || - cm->cmsg_len < CMSG_LEN(*CMSG_DATA(cm))) - return (EINVAL); + /* validate the interface index if specified. */ + if (pktinfo->ipi6_ifindex >= if_indexlim || + pktinfo->ipi6_ifindex < 0) { + return (ENXIO); + } + if (pktinfo->ipi6_ifindex) { + ifp = ifindex2ifnet[pktinfo->ipi6_ifindex]; + if (ifp == NULL) + return (ENXIO); + } + + /* + * We store the address anyway, and let in6_selectsrc() + * validate the specified address. This is because ipi6_addr + * may not have enough information about its scope zone, and + * we may need additional information (such as outgoing + * interface or the scope zone of a destination address) to + * disambiguate the scope. + * XXX: the delay of the validation may confuse the + * application when it is used as a sticky option. + */ + if (opt->ip6po_pktinfo == NULL) { + opt->ip6po_pktinfo = malloc(sizeof(*pktinfo), + M_IP6OPT, M_NOWAIT); + if (opt->ip6po_pktinfo == NULL) + return (ENOBUFS); + } + memcpy(opt->ip6po_pktinfo, pktinfo, sizeof(*pktinfo)); + break; + } + +#ifdef RFC2292 + case IPV6_2292HOPLIMIT: +#endif + case IPV6_HOPLIMIT: + { + int *hlimp; + + /* + * RFC 3542 deprecated the usage of sticky IPV6_HOPLIMIT + * to simplify the ordering among hoplimit options. + */ + if (optname == IPV6_HOPLIMIT && sticky) + return (ENOPROTOOPT); + + if (len != sizeof(int)) + return (EINVAL); + hlimp = (int *)buf; + if (*hlimp < -1 || *hlimp > 255) + return (EINVAL); + + opt->ip6po_hlim = *hlimp; + break; + } + + case IPV6_OTCLASS: + if (len != sizeof(u_int8_t)) + return (EINVAL); + + opt->ip6po_tclass = *(u_int8_t *)buf; + break; + + case IPV6_TCLASS: + { + int tclass; + + if (len != sizeof(int)) + return (EINVAL); + tclass = *(int *)buf; + if (tclass < -1 || tclass > 255) + return (EINVAL); + + opt->ip6po_tclass = tclass; + break; + } - opt->ip6po_nexthop = (struct sockaddr *)CMSG_DATA(cm); +#ifdef RFC2292 + case IPV6_2292NEXTHOP: +#endif + case IPV6_NEXTHOP: + if (!priv) + return (EPERM); + if (len == 0) { /* just remove the option */ + ip6_clearpktopts(opt, IPV6_NEXTHOP); break; + } - case IPV6_HOPOPTS: - if (cm->cmsg_len < CMSG_LEN(sizeof(struct ip6_hbh))) + /* 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 sockaddr_in6 *sa6 = (struct sockaddr_in6 *)buf; + int error; + + if (sa6->sin6_len != sizeof(struct sockaddr_in6)) return (EINVAL); - else { - struct ip6_hbh *t; - t = (struct ip6_hbh *)CMSG_DATA(cm); - if (cm->cmsg_len != - CMSG_LEN((t->ip6h_len + 1) << 3)) - return (EINVAL); - opt->ip6po_hbh = t; + if (IN6_IS_ADDR_UNSPECIFIED(&sa6->sin6_addr) || + IN6_IS_ADDR_MULTICAST(&sa6->sin6_addr)) { + return (EINVAL); + } + if ((error = sa6_embedscope(sa6, ip6_use_defzone)) + != 0) { + return (error); } break; + } + case AF_LINK: /* eventually be supported? */ + default: + return (EAFNOSUPPORT); + } - case IPV6_DSTOPTS: - if (cm->cmsg_len < CMSG_LEN(sizeof(struct ip6_dest))) - return (EINVAL); + /* turn off the previous option, then set the new option. */ + ip6_clearpktopts(opt, IPV6_NEXTHOP); + opt->ip6po_nexthop = malloc(*buf, M_IP6OPT, M_NOWAIT); + if (opt->ip6po_nexthop == NULL) + return (ENOBUFS); + memcpy(opt->ip6po_nexthop, buf, *buf); + break; + +#ifdef RFC2292 + case IPV6_2292HOPOPTS: +#endif + 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 = malloc(hbhlen, M_IP6OPT, M_NOWAIT); + if (opt->ip6po_hbh == NULL) + return (ENOBUFS); + memcpy(opt->ip6po_hbh, hbh, hbhlen); + + break; + } + +#ifdef RFC2292 + case IPV6_2292DSTOPTS: +#endif + case IPV6_DSTOPTS: + case IPV6_RTHDRDSTOPTS: + { + struct ip6_dest *dest, **newdest = NULL; + int destlen; + + if (!priv) /* XXX: see the comment for IPV6_HOPOPTS */ + return (EPERM); + + if (len == 0) { + ip6_clearpktopts(opt, optname); + break; /* just remove the option */ + } + + /* message length validation */ + if (len < sizeof(struct ip6_dest)) + return (EINVAL); + dest = (struct ip6_dest *)buf; + destlen = (dest->ip6d_len + 1) << 3; + if (len != destlen) + return (EINVAL); + /* + * Determine the position that the destination options header + * should be inserted; before or after the routing header. + */ + switch (optname) { + case IPV6_2292DSTOPTS: /* - * If there is no routing header yet, the destination - * options header should be put on the 1st part. - * Otherwise, the header should be on the 2nd part. - * (See RFC 2460, section 4.1) + * The old advanced 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) { - struct ip6_dest *t; - - t = (struct ip6_dest *)CMSG_DATA(cm); - if (cm->cmsg_len != - CMSG_LEN((t->ip6d_len + 1) << 3)); - return (EINVAL); - opt->ip6po_dest1 = t; - } - else { - struct ip6_dest *t; - - t = (struct ip6_dest *)CMSG_DATA(cm); - if (cm->cmsg_len != - CMSG_LEN((opt->ip6po_dest2->ip6d_len + 1) << 3)) - return (EINVAL); - opt->ip6po_dest2 = t; - } + 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; + } - case IPV6_RTHDR: - if (cm->cmsg_len < CMSG_LEN(sizeof(struct ip6_rthdr))) - return (EINVAL); - else { - struct ip6_rthdr *t; + /* turn off the previous option, then set the new option. */ + ip6_clearpktopts(opt, optname); + *newdest = malloc(destlen, M_IP6OPT, M_NOWAIT); + if (*newdest == NULL) + return (ENOBUFS); + memcpy(*newdest, dest, destlen); - t = (struct ip6_rthdr *)CMSG_DATA(cm); - if (cm->cmsg_len != - CMSG_LEN((t->ip6r_len + 1) << 3)) - return (EINVAL); - switch (t->ip6r_type) { - case IPV6_RTHDR_TYPE_0: - if (t->ip6r_segleft == 0) - return (EINVAL); - break; - default: - return (EINVAL); - } - opt->ip6po_rthdr = t; - } - break; + break; + } + +#ifdef RFC2292 + case IPV6_2292RTHDR: +#endif + case IPV6_RTHDR: + { + struct ip6_rthdr *rth; + int rthlen; + if (len == 0) { + ip6_clearpktopts(opt, IPV6_RTHDR); + break; /* just remove the option */ + } + + /* message length validation */ + if (len < sizeof(struct ip6_rthdr)) + return (EINVAL); + rth = (struct ip6_rthdr *)buf; + rthlen = (rth->ip6r_len + 1) << 3; + if (len != rthlen) + return (EINVAL); + switch (rth->ip6r_type) { + case IPV6_RTHDR_TYPE_0: + if (rth->ip6r_len == 0) /* must contain one addr */ + return (EINVAL); + if (rth->ip6r_len % 2) /* length must be even */ + return (EINVAL); + if (rth->ip6r_len / 2 != rth->ip6r_segleft) + return (EINVAL); + break; default: - return (ENOPROTOOPT); + return (EINVAL); /* not supported */ } + /* turn off the previous option */ + ip6_clearpktopts(opt, IPV6_RTHDR); + opt->ip6po_rthdr = malloc(rthlen, M_IP6OPT, M_NOWAIT); + if (opt->ip6po_rthdr == NULL) + return (ENOBUFS); + memcpy(opt->ip6po_rthdr, rth, rthlen); + break; } + case IPV6_USE_MIN_MTU: + if (len != sizeof(int)) + return (EINVAL); + minmtupolicy = *(int *)buf; + if (minmtupolicy != IP6PO_MINMTU_MCASTONLY && + minmtupolicy != IP6PO_MINMTU_DISABLE && + minmtupolicy != IP6PO_MINMTU_ALL) { + return (EINVAL); + } + opt->ip6po_minmtu = minmtupolicy; + break; + + case IPV6_DONTFRAG: + if (len != sizeof(int)) + return (EINVAL); + + if (uproto == IPPROTO_TCP || *(int *)buf == 0) { + /* + * we ignore this option for TCP sockets. + * (RFC3542 leaves this case unspecified.) + */ + opt->ip6po_flags &= ~IP6PO_DONTFRAG; + } else + opt->ip6po_flags |= IP6PO_DONTFRAG; + break; + + default: + return (ENOPROTOOPT); + } /* end of switch */ + return (0); } Index: sys/netinet6/ip6_var.h =================================================================== RCS file: /cvsroot/src/sys/netinet6/ip6_var.h,v retrieving revision 1.36 diff -u -p -r1.36 ip6_var.h --- sys/netinet6/ip6_var.h 5 Mar 2006 23:47:08 -0000 1.36 +++ sys/netinet6/ip6_var.h 29 Apr 2006 11:15:34 -0000 @@ -124,15 +124,34 @@ 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 */ struct in6_pktinfo *ip6po_pktinfo; /* Outgoing IF/address information */ - struct sockaddr *ip6po_nexthop; /* Next-hop address */ + struct ip6po_nhinfo ip6po_nhinfo; /* Next-hop address information */ struct ip6_hbh *ip6po_hbh; /* Hop-by-Hop options header */ struct ip6_dest *ip6po_dest1; /* Destination options header(1st part) */ struct ip6po_rhinfo ip6po_rhinfo; /* Routing header related info. */ struct ip6_dest *ip6po_dest2; /* Destination options header(2nd part) */ + 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_flags; +#if 0 /* parameters in this block is obsolete. do not reuse the values. */ +#define IP6PO_REACHCONF 0x01 /* upper-layer reachability confirmation. */ +#define IP6PO_MINMTU 0x02 /* use minimum MTU (IPV6_USE_MIN_MTU) */ +#endif +#define IP6PO_DONTFRAG 0x04 /* disable fragmentation (IPV6_DONTFRAG) */ }; struct ip6stat { @@ -266,6 +285,7 @@ void ip6_init __P((void)); void ip6intr __P((void)); void ip6_input __P((struct mbuf *)); struct in6_ifaddr *ip6_getdstifaddr __P((struct mbuf *)); +void ip6_freepcbopts __P((struct ip6_pktopts *)); void ip6_freemoptions __P((struct ip6_moptions *)); int ip6_unknown_opt __P((u_int8_t *, struct mbuf *, int)); u_int8_t *ip6_get_prevhdr __P((struct mbuf *, int)); @@ -281,6 +301,8 @@ int ip6_process_hopopts __P((struct mbuf u_int32_t *)); void ip6_savecontrol __P((struct in6pcb *, struct mbuf **, struct ip6_hdr *, struct mbuf *)); +void ip6_notify_pmtu __P((struct in6pcb *, struct sockaddr_in6 *, + u_int32_t *)); int ip6_sysctl __P((int *, u_int, void *, size_t *, void *, size_t)); void ip6_forward __P((struct mbuf *, int)); @@ -292,7 +314,11 @@ int ip6_output __P((struct mbuf *, struc struct ifnet **)); int ip6_ctloutput __P((int, struct socket *, int, int, struct mbuf **)); int ip6_raw_ctloutput __P((int, struct socket *, int, int, struct mbuf **)); -int ip6_setpktoptions __P((struct mbuf *, struct ip6_pktopts *, int)); +void ip6_initpktopts __P((struct ip6_pktopts *)); +int ip6_setpktopts __P((struct mbuf *, struct ip6_pktopts *, + struct ip6_pktopts *, int, int)); +void ip6_clearpktopts __P((struct ip6_pktopts *, int)); +struct ip6_pktopts *ip6_copypktopts __P((struct ip6_pktopts *, int)); int ip6_optlen __P((struct in6pcb *)); int route6_input __P((struct mbuf **, int *, int)); Index: sys/netinet6/raw_ip6.c =================================================================== RCS file: /cvsroot/src/sys/netinet6/raw_ip6.c,v retrieving revision 1.75 diff -u -p -r1.75 raw_ip6.c --- sys/netinet6/raw_ip6.c 21 Jan 2006 00:15:37 -0000 1.75 +++ sys/netinet6/raw_ip6.c 29 Apr 2006 11:15:34 -0000 @@ -350,10 +350,10 @@ rip6_ctlinput(cmd, sa, d) /* * regardless of if we called icmp6_mtudisc_update(), - * we need to call in6_pcbnotify(), to notify path - * MTU change to the userland (2292bis-02), because - * some unconnected sockets may share the same - * destination and want to know the path MTU. + * we need to call in6_pcbnotify(), to notify path MTU + * change to the userland (RFC3542), because some + * unconnected sockets may share the same destination + * and want to know the path MTU. */ } @@ -389,7 +389,6 @@ rip6_output(m, va_alist) int scope_ambiguous = 0; struct in6_addr *in6a; va_list ap; - int flags; va_start(ap, m); so = va_arg(ap, struct socket *); @@ -405,8 +404,11 @@ rip6_output(m, va_alist) dst = &dstsock->sin6_addr; if (control) { - if ((error = ip6_setpktoptions(control, &opt, priv)) != 0) + if ((error = ip6_setpktopts(control, &opt, + in6p->in6p_outputopts, + priv, so->so_proto->pr_protocol)) != 0) { goto bad; + } optp = &opt; } else optp = in6p->in6p_outputopts; @@ -519,11 +521,7 @@ rip6_output(m, va_alist) } } - flags = 0; - if (in6p->in6p_flags & IN6P_MINMTU) - flags |= IPV6_MINMTU; - - error = ip6_output(m, optp, &in6p->in6p_route, flags, + error = ip6_output(m, optp, &in6p->in6p_route, 0, in6p->in6p_moptions, so, &oifp); if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) { if (oifp) @@ -539,10 +537,10 @@ rip6_output(m, va_alist) m_freem(m); freectl: - if (optp == &opt && optp->ip6po_rthdr && optp->ip6po_route.ro_rt) - RTFREE(optp->ip6po_route.ro_rt); - if (control) + if (control) { + ip6_clearpktopts(&opt, -1); m_freem(control); + } return (error); } Index: sys/netinet6/udp6_output.c =================================================================== RCS file: /cvsroot/src/sys/netinet6/udp6_output.c,v retrieving revision 1.23 diff -u -p -r1.23 udp6_output.c --- sys/netinet6/udp6_output.c 21 Jan 2006 00:15:37 -0000 1.23 +++ sys/netinet6/udp6_output.c 29 Apr 2006 11:15:35 -0000 @@ -125,14 +125,14 @@ udp6_output(in6p, m, addr6, control, p) int scope_ambiguous = 0; u_int16_t fport; int error = 0; - struct ip6_pktopts opt, *stickyopt = in6p->in6p_outputopts; + struct ip6_pktopts *optp, opt; int priv; int af = AF_INET6, hlen = sizeof(struct ip6_hdr); #ifdef INET struct ip *ip; struct udpiphdr *ui; -#endif int flags = 0; +#endif struct sockaddr_in6 tmp; priv = 0; @@ -169,10 +169,13 @@ udp6_output(in6p, m, addr6, control, p) } if (control) { - if ((error = ip6_setpktoptions(control, &opt, priv)) != 0) + if ((error = ip6_setpktopts(control, &opt, + in6p->in6p_outputopts, priv, IPPROTO_UDP)) != 0) goto release; - in6p->in6p_outputopts = &opt; - } + optp = &opt; + } else + optp = in6p->in6p_outputopts; + if (sin6) { faddr = &sin6->sin6_addr; @@ -232,7 +235,7 @@ udp6_output(in6p, m, addr6, control, p) } if (!IN6_IS_ADDR_V4MAPPED(faddr)) { - laddr = in6_selectsrc(sin6, in6p->in6p_outputopts, + laddr = in6_selectsrc(sin6, optp, in6p->in6p_moptions, &in6p->in6p_route, &in6p->in6p_laddr, &oifp, &error); if (oifp && scope_ambiguous && @@ -356,12 +359,9 @@ udp6_output(in6p, m, addr6, control, p) m->m_pkthdr.csum_flags = M_CSUM_UDPv6; m->m_pkthdr.csum_data = offsetof(struct udphdr, uh_sum); - if (in6p->in6p_flags & IN6P_MINMTU) - flags |= IPV6_MINMTU; - udp6stat.udp6s_opackets++; - error = ip6_output(m, in6p->in6p_outputopts, &in6p->in6p_route, - flags, in6p->in6p_moptions, in6p->in6p_socket, NULL); + error = ip6_output(m, optp, &in6p->in6p_route, 0, + in6p->in6p_moptions, in6p->in6p_socket, NULL); break; case AF_INET: #ifdef INET @@ -408,7 +408,7 @@ release: releaseopt: if (control) { - in6p->in6p_outputopts = stickyopt; + ip6_clearpktopts(&opt, -1); m_freem(control); } return (error); Index: sys/netinet6/udp6_usrreq.c =================================================================== RCS file: /cvsroot/src/sys/netinet6/udp6_usrreq.c,v retrieving revision 1.73 diff -u -p -r1.73 udp6_usrreq.c --- sys/netinet6/udp6_usrreq.c 21 Jan 2006 00:15:37 -0000 1.73 +++ sys/netinet6/udp6_usrreq.c 29 Apr 2006 11:15:35 -0000 @@ -236,10 +236,11 @@ udp6_ctlinput(cmd, sa, d) icmp6_mtudisc_update((struct ip6ctlparam *)d, valid); /* - * regardless of if we called icmp6_mtudisc_update(), - * we need to call in6_pcbnotify(), to notify path - * MTU change to the userland (2292bis-02), because - * some unconnected sockets may share the same + * regardless of if we called + * icmp6_mtudisc_update(), we need to call + * in6_pcbnotify(), to notify path MTU change + * to the userland (RFC3542), because some + * unconnected sockets may share the same * destination and want to know the path MTU. */ }