? 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 <sys/socket.h>
+
 /*
  * 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.
 			 */
 		}
