Index: ./sys/netinet/in_pcb.h
===================================================================
RCS file: /home/dcvs/src/sys/netinet/in_pcb.h,v
retrieving revision 1.23
diff -u -r1.23 in_pcb.h
--- ./sys/netinet/in_pcb.h	4 Apr 2007 06:13:26 -0000	1.23
+++ ./sys/netinet/in_pcb.h	22 Aug 2008 07:38:12 -0000
@@ -327,16 +327,24 @@
 #define	IN6P_RTHDR		0x100000 /* receive routing header */
 #define	IN6P_RTHDRDSTOPTS	0x200000 /* receive dstoptions before rthdr */
 #define IN6P_AUTOFLOWLABEL	0x800000 /* attach flowlabel automatically */
+/* 
+ * RFC3542 Definition 
+ */
+#define	IN6P_TCLASS		0x400000 /* receive traffic class value */
+#define	IN6P_RFC2292		0x40000000 /* used RFC2292 API on the socket */
+#define	IN6P_MTU		0x80000000 /* receive path MTU */
 
 #define	INP_RECVTTL		0x80000000 /* receive incoming IP TTL */
 
 #define	INP_CONTROLOPTS		(INP_RECVOPTS|INP_RECVRETOPTS|INP_RECVDSTADDR|\
-					INP_RECVIF|INP_RECVTTL|\
+				 INP_RECVIF|INP_RECVTTL|\
 				 IN6P_PKTINFO|IN6P_HOPLIMIT|IN6P_HOPOPTS|\
 				 IN6P_DSTOPTS|IN6P_RTHDR|IN6P_RTHDRDSTOPTS|\
-				 IN6P_AUTOFLOWLABEL)
+				 IN6P_TCLASS|IN6P_AUTOFLOWLABEL|IN6P_RFC2292|\
+				 IN6P_MTU)
+				 
 #define	INP_UNMAPPABLEOPTS	(IN6P_HOPOPTS|IN6P_DSTOPTS|IN6P_RTHDR|\
-				 IN6P_AUTOFLOWLABEL)
+				 IN6P_TCLASS|IN6P_AUTOFLOWLABEL)
 
  /* for KAME src sync over BSD*'s */
 #define	IN6P_HIGHPORT		INP_HIGHPORT
Index: ./sys/netinet/ip6.h
===================================================================
RCS file: /home/dcvs/src/sys/netinet/ip6.h,v
retrieving revision 1.7
diff -u -r1.7 ip6.h
--- ./sys/netinet/ip6.h	23 May 2007 08:57:09 -0000	1.7
+++ ./sys/netinet/ip6.h	11 Aug 2008 08:51:14 -0000
@@ -157,6 +157,7 @@
 #define IP6OPT_TUNNEL_LIMIT	0x04	/* 00 0 00100 */
 #define IP6OPT_RTALERT		0x05	/* 00 0 00101 (KAME definition) */
 
+#define IP6OPT_ROUTER_ALERT	0x05	/* 00 0 00101 (RFC3542, recommended) */
 #define IP6OPT_RTALERT_LEN	4
 #define IP6OPT_RTALERT_MLD	0	/* Datagram contains an MLD message */
 #define IP6OPT_RTALERT_RSVP	1	/* Datagram contains an RSVP message */
@@ -177,8 +178,58 @@
 
 #define IP6OPT_MUTABLE		0x20
 
+/* Jumbo Payload Option */
+struct ip6_opt_jumbo {
+	u_int8_t ip6oj_type;
+	u_int8_t ip6oj_len;
+	u_int8_t ip6oj_jumbo_len[4];
+} __attribute__((__packed__));
 #define IP6OPT_JUMBO_LEN	6
 
+/* IPv6 options: common part */
+struct ip6_opt {
+	u_int8_t ip6o_type;
+	u_int8_t ip6o_len;
+} __attribute__((__packed__));
+
+
+/* NSAP Address Option */
+struct ip6_opt_nsap {
+	u_int8_t ip6on_type;
+	u_int8_t ip6on_len;
+	u_int8_t ip6on_src_nsap_len;
+	u_int8_t ip6on_dst_nsap_len;
+	/* followed by source NSAP */
+	/* followed by destination NSAP */
+} __attribute__((__packed__));
+
+/* Tunnel Limit Option */
+struct ip6_opt_tunnel {
+	u_int8_t ip6ot_type;
+	u_int8_t ip6ot_len;
+	u_int8_t ip6ot_encap_limit;
+} __attribute__((__packed__));
+
+/* Router Alert Option */
+struct ip6_opt_router {
+	u_int8_t ip6or_type;
+	u_int8_t ip6or_len;
+	u_int8_t ip6or_value[2];
+} __attribute__((__packed__));
+
+/* Router alert values (in network byte order) */
+#if BYTE_ORDER == BIG_ENDIAN
+#define IP6_ALERT_MLD	0x0000
+#define IP6_ALERT_RSVP	0x0001
+#define IP6_ALERT_AN	0x0002
+#else
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define IP6_ALERT_MLD	0x0000
+#define IP6_ALERT_RSVP	0x0100
+#define IP6_ALERT_AN	0x0200
+#endif /* LITTLE_ENDIAN */
+#endif
+
 /* Routing header */
 struct ip6_rthdr {
 	u_int8_t  ip6r_nxt;	/* next header */
@@ -188,17 +239,6 @@
 	/* followed by routing type specific data */
 } __attribute__((__packed__));
 
-/* Type 0 Routing header */
-struct ip6_rthdr0 {
-	u_int8_t  ip6r0_nxt;		/* next header */
-	u_int8_t  ip6r0_len;		/* length in units of 8 octets */
-	u_int8_t  ip6r0_type;		/* always zero */
-	u_int8_t  ip6r0_segleft;	/* segments left */
-	u_int8_t  ip6r0_reserved;	/* reserved field */
-	u_int8_t  ip6r0_slmap[3];	/* strict/loose bit map */
-	struct in6_addr  ip6r0_addr[1];	/* up to 23 addresses */
-} __attribute__((__packed__));
-
 /* Fragment header */
 struct ip6_frag {
 	u_int8_t  ip6f_nxt;		/* next header */
Index: ./sys/netinet/icmp6.h
===================================================================
RCS file: /home/dcvs/src/sys/netinet/icmp6.h,v
retrieving revision 1.7
diff -u -r1.7 icmp6.h
--- ./sys/netinet/icmp6.h	20 May 2006 02:42:12 -0000	1.7
+++ ./sys/netinet/icmp6.h	11 Aug 2008 08:27:48 -0000
@@ -115,6 +115,7 @@
 #define MLD_LISTENER_REPORT		131	/* multicast listener report */
 #define ICMP6_MEMBERSHIP_REDUCTION	132	/* group membership termination */
 #define MLD_LISTENER_DONE		132	/* multicast listener done */
+#define MLD_LISTENER_REDUCTION MLD_LISTENER_DONE /* Defined in RFC3542 */
 
 #ifndef _KERNEL
 /* the followings are for backward compatibility to old KAME apps. */
Index: ./sys/netinet6/in6.h
===================================================================
RCS file: /home/dcvs/src/sys/netinet6/in6.h,v
retrieving revision 1.10
diff -u -r1.10 in6.h
--- ./sys/netinet6/in6.h	21 Aug 2007 20:03:20 -0000	1.10
+++ ./sys/netinet6/in6.h	22 Aug 2008 07:37:42 -0000
@@ -395,13 +395,15 @@
 #define IPV6_PORTRANGE		14 /* int; range to choose for unspec port */
 #define ICMP6_FILTER		18 /* icmp6_filter; icmp6 filter */
 /* RFC2292 options */
-#define IPV6_PKTINFO		19 /* bool; send/recv if, src/dst addr */
-#define IPV6_HOPLIMIT		20 /* bool; hop limit */
-#define IPV6_NEXTHOP		21 /* bool; next hop addr */
-#define IPV6_HOPOPTS		22 /* bool; hop-by-hop option */
-#define IPV6_DSTOPTS		23 /* bool; destination option */
-#define IPV6_RTHDR		24 /* bool; routing header */
-#define IPV6_PKTOPTIONS		25 /* buf/cmsghdr; set/get IPv6 options */
+#ifdef _KERNEL
+#define IPV6_2292PKTINFO	19 /* bool; send/recv if, src/dst addr */
+#define IPV6_2292HOPLIMIT	20 /* bool; hop limit */
+#define IPV6_2292NEXTHOP	21 /* bool; next hop addr */
+#define IPV6_2292HOPOPTS	22 /* bool; hop-by-hop option */
+#define IPV6_2292DSTOPTS	23 /* bool; destinaion option */
+#define IPV6_2292RTHDR		24 /* bool; routing header */
+#define IPV6_2292PKTOPTIONS	25 /* buf/cmsghdr; set/get IPv6 options */
+#endif
 
 #define IPV6_CHECKSUM		26 /* int; checksum offset for raw socket */
 #define IPV6_V6ONLY		27 /* bool; only bind INET6 at wildcard bind */
@@ -422,11 +424,58 @@
 #define IPV6_FW_GET		34 /* get entire firewall rule chain */
 #endif
 
+/* 
+ * new socket options introduced in RFC3542 
+ */
+#define IPV6_RTHDRDSTOPTS	35 /* ip6_dest; send dst option before rthdr */
+#define IPV6_RECVPKTINFO	36 /* bool; recv if, dst addr */
+#define IPV6_RECVHOPLIMIT	37 /* bool; recv hop limit */
+#define IPV6_RECVRTHDR		38 /* bool; recv routing header */
+#define IPV6_RECVHOPOPTS	39 /* bool; recv hop-by-hop option */
+#define IPV6_RECVDSTOPTS	40 /* bool; recv dst option after rthdr */
+#ifdef _KERNEL
+#define IPV6_RECVRTHDRDSTOPTS	41 /* bool; recv dst option before rthdr */
+#endif
+#define IPV6_USE_MIN_MTU	42 /* bool; send packets at the minimum MTU */
+#define IPV6_RECVPATHMTU	43 /* bool; notify an according MTU */
+
+/* mtuinfo; get the current path MTU (sopt),
+ * 4 bytes int; MTU notification (cmsg) 
+ */
+#define IPV6_PATHMTU		44 
+#if 0 /*obsoleted during 2292bis -> 3542*/
+#define IPV6_REACHCONF		45 /* no data; ND reachability confirm
+				      (cmsg only/not in of RFC3542) */
+#endif
+#define IPV6_PKTINFO		46 /* in6_pktinfo; send if, src addr */
+#define IPV6_HOPLIMIT		47 /* int; send hop limit */
+#define IPV6_NEXTHOP		48 /* sockaddr; next hop addr */
+#define IPV6_HOPOPTS		49 /* ip6_hbh; send hop-by-hop option */
+#define IPV6_DSTOPTS		50 /* ip6_dest; send dst option befor rthdr */
+#define IPV6_RTHDR		51 /* ip6_rthdr; send routing header */
+#define IPV6_PKTOPTIONS		52 /* buf/cmsghdr; set/get IPv6 options, this is obsoleted by RFC3542 */
+
+#define IPV6_RECVTCLASS		57 /* bool; recv traffic class values */
+
+#define IPV6_AUTOFLOWLABEL	59 /* bool; attach flowlabel automagically */
+
+#define IPV6_TCLASS		61 /* int; send traffic class value */
+#define IPV6_DONTFRAG		62 /* bool; disable IPv6 fragmentation */
+
+#define IPV6_PREFER_TEMPADDR	63 /* int; prefer temporary addresses as
+				    * the source address.
+				    */
+/*
+ * The following option is private; do not use it from user applications.
+ * It is deliberately defined to the same value as IP_MSFILTER.
+ */
+#define	IPV6_MSFILTER		74 /* struct __msfilterreq;
+				    * set/get multicast source filter list.
+				    */ 
 /* to define items, should talk with KAME guys first, for *BSD compatibility */
 
 #define IPV6_RTHDR_LOOSE     0 /* this hop need not be a neighbor. XXX old spec */
 #define IPV6_RTHDR_STRICT    1 /* this hop must be a neighbor. XXX old spec */
-#define IPV6_RTHDR_TYPE_0    0 /* IPv6 routing header type 0 */
 
 /*
  * Defaults and limits for options
@@ -451,6 +500,14 @@
 };
 
 /*
+ * New Control structure for IPV6_RECVPATHMTU socket option introduced in RFC3542.
+ */
+struct ip6_mtuinfo {
+	struct sockaddr_in6 ip6m_addr;	/* or sockaddr_storage? */
+	uint32_t ip6m_mtu;
+};
+
+/*
  * Argument for IPV6_PORTRANGE:
  * - which range to search when port is unspecified at bind() or connect()
  */
Index: ./sys/netinet6/ip6_output.c
===================================================================
RCS file: /home/dcvs/src/sys/netinet6/ip6_output.c,v
retrieving revision 1.36
diff -u -r1.36 ip6_output.c
--- ./sys/netinet6/ip6_output.c	18 Jun 2008 11:38:37 -0000	1.36
+++ ./sys/netinet6/ip6_output.c	24 Aug 2008 02:30:12 -0000
@@ -94,6 +94,7 @@
 #include <netinet6/ip6_var.h>
 #include <netinet/in_pcb.h>
 #include <netinet6/nd6.h>
+#include <netinet6/ip6protosw.h>
 
 #ifdef IPSEC
 #include <netinet6/ipsec.h>
@@ -123,15 +124,22 @@
 	struct mbuf *ip6e_dest2;
 };
 
+static int ip6_pcbopt (int, u_char *, int, struct ip6_pktopts **, int);
+static int ip6_setpktoption (int, u_char *, int, struct ip6_pktopts *,
+	 int, int, int, int);
 static int ip6_pcbopts (struct ip6_pktopts **, struct mbuf *,
-			    struct socket *, struct sockopt *sopt);
+			    struct socket *, struct sockopt *);
+static int ip6_getpcbopt(struct ip6_pktopts *, int, struct sockopt *);
 static int ip6_setmoptions (int, struct ip6_moptions **, struct mbuf *);
 static int ip6_getmoptions (int, struct ip6_moptions *, struct mbuf **);
+static int ip6_getpmtu(struct route_in6 *, struct route_in6 *,
+	struct ifnet *, struct in6_addr *, u_long *, int *);
 static int copyexthdr (void *, struct mbuf **);
 static int ip6_insertfraghdr (struct mbuf *, struct mbuf *, int,
 				  struct ip6_frag **);
 static int ip6_insert_jumboopt (struct ip6_exthdrs *, u_int32_t);
 static struct mbuf *ip6_splithdr (struct mbuf *);
+static int copypktopts(struct ip6_pktopts *, struct ip6_pktopts *, int);
 
 /*
  * IP6 output. The packet in mbuf chain m contains a skeletal IP6
@@ -161,6 +169,7 @@
 	int error = 0;
 	struct in6_ifaddr *ia = NULL;
 	u_long mtu;
+	int alwaysfrag, dontfrag;
 	u_int32_t optlen, plen = 0, unfragpartlen;
 	struct ip6_exthdrs exthdrs;
 	struct in6_addr finaldst;
@@ -447,18 +456,10 @@
 	 */
 	if (exthdrs.ip6e_rthdr) {
 		struct ip6_rthdr *rh;
-		struct ip6_rthdr0 *rh0;
 
 		finaldst = ip6->ip6_dst;
 		rh = mtod(exthdrs.ip6e_rthdr, struct ip6_rthdr *);
 		switch (rh->ip6r_type) {
-		case IPV6_RTHDR_TYPE_0:
-			 rh0 = (struct ip6_rthdr0 *)rh;
-			 ip6->ip6_dst = rh0->ip6r0_addr[0];
-			 bcopy(&rh0->ip6r0_addr[1], &rh0->ip6r0_addr[0],
-			     sizeof(struct in6_addr)*(rh0->ip6r0_segleft - 1));
-			 rh0->ip6r0_addr[rh0->ip6r0_segleft - 1] = finaldst;
-			 break;
 		default:	/* is it possible? */
 			 error = EINVAL;
 			 goto bad;
@@ -745,59 +746,35 @@
 	if (ifpp)
 		*ifpp = ifp;
 
-	/*
-	 * Determine path MTU.
-	 */
-	if (ro_pmtu != ro) {
-		/* The first hop and the final destination may differ. */
-		struct sockaddr_in6 *sin6_fin =
-			(struct sockaddr_in6 *)&ro_pmtu->ro_dst;
-
-		if (ro_pmtu->ro_rt != NULL &&
-		    (!(ro->ro_rt->rt_flags & RTF_UP) ||
-		     !IN6_ARE_ADDR_EQUAL(&sin6_fin->sin6_addr, &finaldst))) {
-			RTFREE(ro_pmtu->ro_rt);
-			ro_pmtu->ro_rt = NULL;
-		}
-		if (ro_pmtu->ro_rt == NULL) {
-			bzero(sin6_fin, sizeof(*sin6_fin));
-			sin6_fin->sin6_family = AF_INET6;
-			sin6_fin->sin6_len = sizeof(struct sockaddr_in6);
-			sin6_fin->sin6_addr = finaldst;
-
-			rtalloc((struct route *)ro_pmtu);
-		}
-	}
-	if (ro_pmtu->ro_rt != NULL) {
-		u_int32_t ifmtu = ND_IFINFO(ifp)->linkmtu;
+	/* Determine path MTU. */
+	if ((error = ip6_getpmtu(ro_pmtu, ro, ifp, &finaldst, &mtu,
+	    &alwaysfrag)) != 0)
+		goto bad;
 
-		mtu = ro_pmtu->ro_rt->rt_rmx.rmx_mtu;
-		if (mtu > ifmtu || mtu == 0) {
-			/*
-			 * The MTU on the route is larger than the MTU on
-			 * the interface!  This shouldn't happen, unless the
-			 * MTU of the interface has been changed after the
-			 * interface was brought up.  Change the MTU in the
-			 * route to match the interface MTU (as long as the
-			 * field isn't locked).
-			 *
-			 * if MTU on the route is 0, we need to fix the MTU.
-			 * this case happens with path MTU discovery timeouts.
-			 */
-			 mtu = ifmtu;
-			 if (!(ro_pmtu->ro_rt->rt_rmx.rmx_locks & RTV_MTU))
-				 ro_pmtu->ro_rt->rt_rmx.rmx_mtu = mtu; /* XXX */
+	/*
+	 * 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;
 		}
-	} else {
-		mtu = ND_IFINFO(ifp)->linkmtu;
 	}
 
-	/*
-	 * advanced API (IPV6_USE_MIN_MTU) overrides mtu setting
-	 */
-	if ((flags & IPV6_MINMTU) != 0 && mtu > IPV6_MMTU)
-		mtu = IPV6_MMTU;
-
 	/* Fake scoped addresses */
 	if ((ifp->if_flags & IFF_LOOPBACK) != 0) {
 		/*
@@ -909,36 +886,80 @@
 	/*
 	 * Send the packet to the outgoing interface.
 	 * If necessary, do IPv6 fragmentation before sending.
+	 *
+	 * the logic here is rather complex:
+	 * 1: normal case (dontfrag == 0, alwaysfrag == 0)
+	 * 1-a:	send as is if tlen <= path mtu
+	 * 1-b:	fragment if tlen > path mtu
+	 *
+	 * 2: if user asks us not to fragment (dontfrag == 1)
+	 * 2-a:	send as is if tlen <= interface mtu
+	 * 2-b:	error if tlen > interface mtu
+	 *
+	 * 3: if we always need to attach fragment header (alwaysfrag == 1)
+	 *	always fragment
+	 *
+	 * 4: if dontfrag == 1 && alwaysfrag == 1
+	 *	error, as we cannot handle this conflicting request
 	 */
 	tlen = m->m_pkthdr.len;
-	if (tlen <= mtu
-#ifdef notyet
-	    /*
-	     * On any link that cannot convey a 1280-octet packet in one piece,
-	     * link-specific fragmentation and reassembly must be provided at
-	     * a layer below IPv6. [RFC 2460, sec.5]
-	     * Thus if the interface has ability of link-level fragmentation,
-	     * we can just send the packet even if the packet size is
-	     * larger than the link's MTU.
-	     * XXX: IFF_FRAGMENTABLE (or such) flag has not been defined yet...
-	     */
 
-	    || ifp->if_flags & IFF_FRAGMENTABLE
-#endif
-	    )
-	{
- 		/* Record statistics for this interface address. */
- 		if (ia && !(flags & IPV6_FORWARDING)) {
- 			ia->ia_ifa.if_opackets++;
- 			ia->ia_ifa.if_obytes += m->m_pkthdr.len;
- 		}
+	if (opt && (opt->ip6po_flags & IP6PO_DONTFRAG))
+		dontfrag = 1;
+	else
+		dontfrag = 0;
+	if (dontfrag && alwaysfrag) {	/* case 4 */
+		/* conflicting request - can't transmit */
+		error = EMSGSIZE;
+		goto bad;
+	}
+	if (dontfrag && tlen > IN6_LINKMTU(ifp)) {	/* case 2-b */
+		/*
+		 * Even if the DONTFRAG option is specified, we cannot send the
+		 * packet when the data length is larger than the MTU of the
+		 * outgoing interface.
+		 * Notify the error by sending IPV6_PATHMTU ancillary data as
+		 * well as returning an error code (the latter is not described
+		 * in the API spec.)
+		 */
+		u_int32_t mtu32;
+		struct ip6ctlparam ip6cp;
+
+		mtu32 = (u_int32_t)mtu;
+		bzero(&ip6cp, sizeof(ip6cp));
+		ip6cp.ip6c_cmdarg = (void *)&mtu32;
+		kpfctlinput2(PRC_MSGSIZE, (struct sockaddr *)&ro_pmtu->ro_dst,
+		    (void *)&ip6cp);
+
+		error = EMSGSIZE;
+		goto bad;
+	}
+
+	/*
+	 * transmit packet without fragmentation
+	 */
+	if (dontfrag || (!alwaysfrag && tlen <= mtu)) {	/* case 1-a and 2-a */
+		struct in6_ifaddr *ia6;
+
+		ip6 = mtod(m, struct ip6_hdr *);
+		ia6 = in6_ifawithifp(ifp, &ip6->ip6_src);
+		if (ia6) {
+			/* Record statistics for this interface address. */
+			ia6->ia_ifa.if_opackets++;
+			ia6->ia_ifa.if_obytes += m->m_pkthdr.len;
+		}
 #ifdef IPSEC
 		/* clean ipsec history once it goes out of the node */
 		ipsec_delaux(m);
 #endif
 		error = nd6_output(ifp, origifp, m, dst, ro->ro_rt);
 		goto done;
-	} else if (mtu < IPV6_MMTU) {
+	} 
+
+	/*
+	 * try to fragment the packet.  case 1-b and 3
+	 */
+	if (mtu < IPV6_MMTU) {
 		/*
 		 * note that path MTU is never less than IPV6_MMTU
 		 * (see icmp6_input).
@@ -954,6 +975,7 @@
 		struct mbuf **mnext, *m_frgpart;
 		struct ip6_frag *ip6f;
 		u_int32_t id = htonl(ip6_id++);
+		int qslots = ifp->if_snd.ifq_maxlen - ifp->if_snd.ifq_len;
 		u_char nextproto;
 
 		/*
@@ -972,6 +994,17 @@
 			goto bad;
 		}
 
+		/*
+		 * Verify that we have any chance at all of being able to queue
+		 *      the packet or packet fragments
+		 */
+		if (qslots <= 0 || ((u_int)qslots * (mtu - hlen)
+		    < tlen  /* - hlen */)) {
+			error = ENOBUFS;
+			ip6stat.ip6s_odropped++;
+			goto bad;
+		}
+
 		mnext = &m->m_nextpkt;
 
 		/*
@@ -1257,14 +1290,95 @@
 	return (0);
 }
 
+/* 
+ * new function that was introduced in due to RFC3542 
+ */
+static int
+ip6_getpmtu(struct route_in6 *ro_pmtu, struct route_in6 *ro,
+    struct ifnet *ifp, struct in6_addr *dst, u_long *mtup,
+    int *alwaysfragp)
+{
+	u_int32_t mtu = 0;
+	int alwaysfrag = 0;
+	int error = 0;
+
+	if (ro_pmtu != ro) {
+		/* The first hop and the final destination may differ. */
+		struct sockaddr_in6 *sa6_dst =
+		    (struct sockaddr_in6 *)&ro_pmtu->ro_dst;
+		if (ro_pmtu->ro_rt &&
+		    ((ro_pmtu->ro_rt->rt_flags & RTF_UP) == 0 ||
+		     !IN6_ARE_ADDR_EQUAL(&sa6_dst->sin6_addr, dst))) {
+			RTFREE(ro_pmtu->ro_rt);
+			ro_pmtu->ro_rt = (struct rtentry *)NULL;
+		}
+		if (ro_pmtu->ro_rt == NULL) {
+			bzero(sa6_dst, sizeof(*sa6_dst));
+			sa6_dst->sin6_family = AF_INET6;
+			sa6_dst->sin6_len = sizeof(struct sockaddr_in6);
+			sa6_dst->sin6_addr = *dst;
+
+			rtalloc((struct route *)ro_pmtu);
+		}
+	}
+	if (ro_pmtu->ro_rt) {
+		u_int32_t ifmtu;
+		struct in_conninfo inc;
+
+		bzero(&inc, sizeof(inc));
+		inc.inc_flags = 1; /* IPv6 */
+		inc.inc6_faddr = *dst;
+
+		if (ifp == NULL)
+			ifp = ro_pmtu->ro_rt->rt_ifp;
+		ifmtu = IN6_LINKMTU(ifp);
+		mtu = ro_pmtu->ro_rt->rt_rmx.rmx_mtu;
+		if (mtu == 0)
+			mtu = ifmtu;
+		else if (mtu < IPV6_MMTU) {
+			/*
+			 * RFC2460 section 5, last paragraph:
+			 * if we record ICMPv6 too big message with
+			 * mtu < IPV6_MMTU, transmit packets sized IPV6_MMTU
+			 * or smaller, with framgent header attached.
+			 * (fragment header is needed regardless from the
+			 * packet size, for translators to identify packets)
+			 */
+			alwaysfrag = 1;
+			mtu = IPV6_MMTU;
+		} else if (mtu > ifmtu) {
+			/*
+			 * The MTU on the route is larger than the MTU on
+			 * the interface!  This shouldn't happen, unless the
+			 * MTU of the interface has been changed after the
+			 * interface was brought up.  Change the MTU in the
+			 * route to match the interface MTU (as long as the
+			 * field isn't locked).
+			 */
+			mtu = ifmtu;
+			ro_pmtu->ro_rt->rt_rmx.rmx_mtu = mtu;
+		}
+	} else if (ifp) {
+		mtu = IN6_LINKMTU(ifp);
+	} else
+		error = EHOSTUNREACH; /* XXX */
+
+	*mtup = mtu;
+	if (alwaysfragp)
+		*alwaysfragp = alwaysfrag;
+	return (error);
+}
+
 /*
  * IP6 socket option processing.
  */
 int
 ip6_ctloutput(struct socket *so, struct sockopt *sopt)
 {
+	int optdatalen,uproto;
 	int privileged;
 	struct inpcb *in6p = so->so_pcb;
+	void *optdata;
 	int error, optval;
 	int level, op, optname;
 	int optlen;
@@ -1282,7 +1396,8 @@
 		td = NULL;
 	}
 	error = optval = 0;
-
+	
+	uproto = (int)so->so_proto->pr_protocol;
 	privileged = (td == NULL || suser(td)) ? 0 : 1;
 
 	if (level == IPPROTO_IPV6) {
@@ -1290,7 +1405,10 @@
 
 		case SOPT_SET:
 			switch (optname) {
-			case IPV6_PKTOPTIONS:
+			case IPV6_2292PKTOPTIONS:
+#ifdef IPV6_PKTOPTIONS
+			case IPV6_PKTOPTIONS: /*RFC3542 has obsolete IPV6_PKTOPTIONS and add some new socket option*/
+#endif
 			{
 				struct mbuf *m;
 
@@ -1317,8 +1435,23 @@
 			 *  receiving ANY hbh/dst options in order to avoid
 			 *  overhead of parsing options in the kernel.
 			 */
+			/*
+			 * some new socket option introduced in RFC3542
+			 */
+			case IPV6_RECVHOPOPTS:
+			case IPV6_RECVDSTOPTS:
+			case IPV6_RECVRTHDRDSTOPTS:
+				if (!privileged)
+					return (EPERM);
+			case IPV6_RECVPKTINFO:
+			case IPV6_RECVHOPLIMIT:
+			case IPV6_RECVRTHDR:
+			case IPV6_RECVPATHMTU:
+			case IPV6_RECVTCLASS:
+			case IPV6_AUTOFLOWLABEL:
+			case IPV6_HOPLIMIT:
+			/* FALLTHROUGH */
 			case IPV6_UNICAST_HOPS:
-			case IPV6_CHECKSUM:
 			case IPV6_FAITH:
 
 			case IPV6_V6ONLY:
@@ -1352,9 +1485,111 @@
 		in6p->in6p_flags &= ~(bit); \
 } while (0)
 #define OPTBIT(bit) (in6p->in6p_flags & (bit) ? 1 : 0)
+/* 
+ * Although changed to RFC3542, It's better to also support RFC2292 API 
+ */
+#define OPTSET2292(bit) \
+do { \
+	in6p->in6p_flags |= IN6P_RFC2292; \
+	if (optval) \
+		in6p->in6p_flags |= (bit); \
+	else \
+		in6p->in6p_flags &= ~(bit); \
+} while (/*CONSTCOND*/ 0)
+
+				/* introduced in RFC3542*/
+				case IPV6_RECVPKTINFO:
+					/* cannot mix with RFC2292 */
+					if (OPTBIT(IN6P_RFC2292)) {
+						error = EINVAL;
+						break;
+					}
+					OPTSET(IN6P_PKTINFO);
+					break;
+
+				case IPV6_HOPLIMIT:
+				{
+					struct ip6_pktopts **optp;
+
+					/* cannot mix with RFC2292 */
+					if (OPTBIT(IN6P_RFC2292)) {
+						error = EINVAL;
+						break;
+					}
+					optp = &in6p->in6p_outputopts;
+					error = ip6_pcbopt(IPV6_HOPLIMIT,
+					    (u_char *)&optval, sizeof(optval),
+					    optp, uproto);
+					break;
+				}
+
+				case IPV6_RECVHOPLIMIT:
+					/* cannot mix with RFC2292 */
+					if (OPTBIT(IN6P_RFC2292)) {
+						error = EINVAL;
+						break;
+					}
+					OPTSET(IN6P_HOPLIMIT);
+					break;
+
+				case IPV6_RECVHOPOPTS:
+					/* cannot mix with RFC2292 */
+					if (OPTBIT(IN6P_RFC2292)) {
+						error = EINVAL;
+						break;
+					}
+					OPTSET(IN6P_HOPOPTS);
+					break;
+
+				case IPV6_RECVDSTOPTS:
+					/* cannot mix with RFC2292 */
+					if (OPTBIT(IN6P_RFC2292)) {
+						error = EINVAL;
+						break;
+					}
+					OPTSET(IN6P_DSTOPTS);
+					break;
+
+				case IPV6_RECVRTHDRDSTOPTS:
+					/* cannot mix with RFC2292 */
+					if (OPTBIT(IN6P_RFC2292)) {
+						error = EINVAL;
+						break;
+					}
+					OPTSET(IN6P_RTHDRDSTOPTS);
+					break;
+
+				case IPV6_RECVRTHDR:
+					/* cannot mix with RFC2292 */
+					if (OPTBIT(IN6P_RFC2292)) {
+						error = EINVAL;
+						break;
+					}
+					OPTSET(IN6P_RTHDR);
+					break;
+
+				case IPV6_RECVPATHMTU:
+					/*
+					 * We ignore this option for TCP
+					 * sockets.
+					 * (RFC3542 leaves this case
+					 * unspecified.)
+					 */
+					if (uproto != IPPROTO_TCP)
+						OPTSET(IN6P_MTU);
+					break;
+
+				case IPV6_RECVTCLASS:
+					/* cannot mix with RFC2292 XXX */
+					if (OPTBIT(IN6P_RFC2292)) {
+						error = EINVAL;
+						break;
+					}
+					OPTSET(IN6P_TCLASS);
+					break;
 
-				case IPV6_CHECKSUM:
-					in6p->in6p_cksum = optval;
+				case IPV6_AUTOFLOWLABEL:
+					OPTSET(IN6P_AUTOFLOWLABEL);
 					break;
 
 				case IPV6_FAITH:
@@ -1365,7 +1600,6 @@
 					/*
 					 * make setsockopt(IPV6_V6ONLY)
 					 * available only prior to bind(2).
-					 * see ipng mailing list, Jun 22 2001.
 					 */
 					if (in6p->in6p_lport ||
 					    !IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr))
@@ -1381,12 +1615,35 @@
 					break;
 				}
 				break;
+			/* 
+			 * options introduced in RFC3542
+			 */
+			case IPV6_TCLASS:
+			case IPV6_DONTFRAG:
+			case IPV6_USE_MIN_MTU:
+			case IPV6_PREFER_TEMPADDR:
+				if (optlen != sizeof(optval)) {
+					error = EINVAL;
+					break;
+				}
+				error = soopt_to_kbuf(sopt, &optval,
+					sizeof optval, sizeof optval);
+				if (error)
+					break;
+				{
+					struct ip6_pktopts **optp;
+					optp = &in6p->in6p_outputopts;
+					error = ip6_pcbopt(optname,
+					    (u_char *)&optval, sizeof(optval),
+					    optp, uproto);
+					break;
+				}
 
-			case IPV6_PKTINFO:
-			case IPV6_HOPLIMIT:
-			case IPV6_HOPOPTS:
-			case IPV6_DSTOPTS:
-			case IPV6_RTHDR:
+			case IPV6_2292PKTINFO:
+			case IPV6_2292HOPLIMIT:
+			case IPV6_2292HOPOPTS:
+			case IPV6_2292DSTOPTS:
+			case IPV6_2292RTHDR:
 				/* RFC 2292 */
 				if (optlen != sizeof(int)) {
 					error = EINVAL;
@@ -1397,31 +1654,69 @@
 				if (error)
 					break;
 				switch (optname) {
-				case IPV6_PKTINFO:
-					OPTSET(IN6P_PKTINFO);
+				case IPV6_2292PKTINFO:
+					OPTSET2292(IN6P_PKTINFO);
 					break;
-				case IPV6_HOPLIMIT:
-					OPTSET(IN6P_HOPLIMIT);
+				case IPV6_2292HOPLIMIT:
+					OPTSET2292(IN6P_HOPLIMIT);
 					break;
-				case IPV6_HOPOPTS:
+				case IPV6_2292HOPOPTS:
 					/*
 					 * Check super-user privilege.
 					 * See comments for IPV6_RECVHOPOPTS.
 					 */
 					if (!privileged)
 						return (EPERM);
-					OPTSET(IN6P_HOPOPTS);
+					OPTSET2292(IN6P_HOPOPTS);
 					break;
-				case IPV6_DSTOPTS:
+				case IPV6_2292DSTOPTS:
 					if (!privileged)
 						return (EPERM);
-					OPTSET(IN6P_DSTOPTS|IN6P_RTHDRDSTOPTS); /* XXX */
+					OPTSET2292(IN6P_DSTOPTS|IN6P_RTHDRDSTOPTS); /* XXX */
 					break;
-				case IPV6_RTHDR:
-					OPTSET(IN6P_RTHDR);
+				case IPV6_2292RTHDR:
+					OPTSET2292(IN6P_RTHDR);
+					break;
+				}
+				break;
+
+			case IPV6_PKTINFO:
+			case IPV6_HOPOPTS:
+			case IPV6_RTHDR:
+			case IPV6_DSTOPTS:
+			case IPV6_RTHDRDSTOPTS:
+			case IPV6_NEXTHOP:
+			{
+				/* 
+				 * new advanced API (RFC3542) 
+				 */
+				u_char *optbuf;
+				u_char optbuf_storage[MCLBYTES];
+				int optlen;
+				struct ip6_pktopts **optp;
+
+				/* cannot mix with RFC2292 */
+				if (OPTBIT(IN6P_RFC2292)) {
+					error = EINVAL;
 					break;
 				}
+
+				/*
+				 * We only ensure valsize is not too large
+				 * here.  Further validation will be done
+				 * later.
+				 */
+				error = soopt_to_kbuf(sopt, optbuf_storage,
+				    sizeof(optbuf_storage), 0);
+				if (error)
+					break;
+				optlen = sopt->sopt_valsize;
+				optbuf = optbuf_storage;
+				optp = &in6p->in6p_outputopts;
+				error = ip6_pcbopt(optname, optbuf, optlen,
+				    optp, uproto);
 				break;
+			}	
 #undef OPTSET
 
 			case IPV6_MULTICAST_IF:
@@ -1528,8 +1823,19 @@
 
 		case SOPT_GET:
 			switch (optname) {
-
+			case IPV6_2292PKTOPTIONS:
+#ifdef IPV6_PKTOPTIONS
 			case IPV6_PKTOPTIONS:
+#endif
+				/*
+				 * RFC3542 (effectively) deprecated the
+				 * semantics of the 2292-style pktoptions.
+				 * Since it was not reliable in nature (i.e.,
+				 * applications had to expect the lack of some
+				 * information after all), it would make sense
+				 * to simplify this part by always returning
+				 * empty data.
+				 */
 				if (in6p->in6p_options) {
 					struct mbuf *m;
 					m = m_copym(in6p->in6p_options,
@@ -1541,20 +1847,65 @@
 					sopt->sopt_valsize = 0;
 				break;
 
-			case IPV6_UNICAST_HOPS:
-			case IPV6_CHECKSUM:
 
+		
+			/* 
+			 * socket options introduced in RFC3542
+			 */
+			case IPV6_RECVHOPOPTS:
+			case IPV6_RECVDSTOPTS:
+			case IPV6_RECVRTHDRDSTOPTS:
+			case IPV6_UNICAST_HOPS:
+			case IPV6_RECVPKTINFO:
+			case IPV6_RECVHOPLIMIT:
+			case IPV6_RECVRTHDR:
+			case IPV6_RECVPATHMTU:
+			case IPV6_RECVTCLASS:
+			case IPV6_AUTOFLOWLABEL:
 			case IPV6_FAITH:
 			case IPV6_V6ONLY:
 			case IPV6_PORTRANGE:
 				switch (optname) {
 
-				case IPV6_UNICAST_HOPS:
-					optval = in6p->in6p_hops;
+				case IPV6_RECVHOPOPTS:
+					optval = OPTBIT(IN6P_HOPOPTS);
+					break;
+
+				case IPV6_RECVDSTOPTS:
+					optval = OPTBIT(IN6P_DSTOPTS);
+					break;
+
+				case IPV6_RECVRTHDRDSTOPTS:
+					optval = OPTBIT(IN6P_RTHDRDSTOPTS);
+					break;
+
+				case IPV6_RECVPKTINFO:
+					optval = OPTBIT(IN6P_PKTINFO);
+					break;
+
+				case IPV6_RECVHOPLIMIT:
+					optval = OPTBIT(IN6P_HOPLIMIT);
+					break;
+
+				case IPV6_RECVRTHDR:
+					optval = OPTBIT(IN6P_RTHDR);
+					break;
+
+				case IPV6_RECVPATHMTU:
+					optval = OPTBIT(IN6P_MTU);
+					break;
+
+				case IPV6_RECVTCLASS:
+					optval = OPTBIT(IN6P_TCLASS);
+					break;
+
+				case IPV6_AUTOFLOWLABEL:
+					optval = OPTBIT(IN6P_AUTOFLOWLABEL);
 					break;
 
-				case IPV6_CHECKSUM:
-					optval = in6p->in6p_cksum;
+
+				case IPV6_UNICAST_HOPS:
+					optval = in6p->in6p_hops;
 					break;
 
 				case IPV6_FAITH:
@@ -1579,41 +1930,92 @@
 				    }
 				}
 				soopt_from_kbuf(sopt, &optval,
-					sizeof optval);
+ 					sizeof optval);
+				break;
+			/*
+			 * now socket option introduced in RFC3542
+			 */
+			case IPV6_PATHMTU:
+			{
+				u_long pmtu = 0;
+				struct ip6_mtuinfo mtuinfo;
+				struct route_in6 sro;
+
+				bzero(&sro, sizeof(sro));
+
+				if (!(so->so_state & SS_ISCONNECTED))
+					return (ENOTCONN);
+				/*
+				 * XXX: we dot not consider the case of source
+				 * routing, or optional information to specify
+				 * the outgoing interface.
+				 */
+				error = ip6_getpmtu(&sro, NULL, NULL,
+				    &in6p->in6p_faddr, &pmtu, NULL);
+				if (sro.ro_rt)
+					RTFREE(sro.ro_rt);
+				if (error)
+					break;
+				if (pmtu > IPV6_MAXPACKET)
+					pmtu = IPV6_MAXPACKET;
+
+				bzero(&mtuinfo, sizeof(mtuinfo));
+				mtuinfo.ip6m_mtu = (u_int32_t)pmtu;
+				optdata = (void *)&mtuinfo;
+				optdatalen = sizeof(mtuinfo);
+				soopt_from_kbuf(sopt, optdata,
+				    optdatalen);
 				break;
+			}
 
-			case IPV6_PKTINFO:
-			case IPV6_HOPLIMIT:
-			case IPV6_HOPOPTS:
-			case IPV6_RTHDR:
-			case IPV6_DSTOPTS:
-				if (optname == IPV6_HOPOPTS ||
-				    optname == IPV6_DSTOPTS ||
+			case IPV6_2292PKTINFO:
+			case IPV6_2292HOPLIMIT:
+			case IPV6_2292HOPOPTS:
+			case IPV6_2292RTHDR:
+			case IPV6_2292DSTOPTS:
+				if (optname == IPV6_2292HOPOPTS ||
+				    optname == IPV6_2292DSTOPTS ||
 				    !privileged)
 					return (EPERM);
 				switch (optname) {
-				case IPV6_PKTINFO:
+				case IPV6_2292PKTINFO:
 					optval = OPTBIT(IN6P_PKTINFO);
 					break;
-				case IPV6_HOPLIMIT:
+				case IPV6_2292HOPLIMIT:
 					optval = OPTBIT(IN6P_HOPLIMIT);
 					break;
-				case IPV6_HOPOPTS:
+				case IPV6_2292HOPOPTS:
 					if (!privileged)
 						return (EPERM);
 					optval = OPTBIT(IN6P_HOPOPTS);
 					break;
-				case IPV6_RTHDR:
+				case IPV6_2292RTHDR:
 					optval = OPTBIT(IN6P_RTHDR);
 					break;
-				case IPV6_DSTOPTS:
+				case IPV6_2292DSTOPTS:
 					if (!privileged)
 						return (EPERM);
 					optval = OPTBIT(IN6P_DSTOPTS|IN6P_RTHDRDSTOPTS);
 					break;
 				}
 				soopt_from_kbuf(sopt, &optval,
-					sizeof optval);
+ 					sizeof optval);
+				break;
+			/*
+			 * now socket options introduced in RFC3542
+			 */
+			case IPV6_PKTINFO:
+			case IPV6_HOPOPTS:
+			case IPV6_RTHDR:
+			case IPV6_DSTOPTS:
+			case IPV6_RTHDRDSTOPTS:
+			case IPV6_NEXTHOP:
+			case IPV6_TCLASS:
+			case IPV6_DONTFRAG:
+			case IPV6_USE_MIN_MTU:
+			case IPV6_PREFER_TEMPADDR:
+				error = ip6_getpcbopt(in6p->in6p_outputopts,
+				    optname, sopt);
 				break;
 
 			case IPV6_MULTICAST_IF:
@@ -1627,7 +2029,7 @@
 						in6p->in6p_moptions, &m);
 				if (error == 0)
 					soopt_from_kbuf(sopt,
-						mtod(m, char *), m->m_len);
+ 						mtod(m, char *), m->m_len);
 				m_freem(m);
 			    }
 				break;
@@ -1686,33 +2088,106 @@
 	return (error);
 }
 
-/*
- * Set up IP6 options in pcb for insertion in output packets or
- * specifying behavior of outgoing packets.
- */
-static int
-ip6_pcbopts(struct ip6_pktopts **pktopt, struct mbuf *m, struct socket *so,
-	    struct sockopt *sopt)
+int
+ip6_raw_ctloutput(struct socket *so, struct sockopt *sopt)
 {
-	struct ip6_pktopts *opt = *pktopt;
-	int error = 0;
-	struct thread *td = sopt->sopt_td;
-	int priv = 0;
+	int error = 0, optval, optlen;
+	const int icmp6off = offsetof(struct icmp6_hdr, icmp6_cksum);
+	struct in6pcb *in6p = sotoin6pcb(so);
+	int level, op, optname;
 
-	/* turn off any old options. */
-	if (opt) {
-#ifdef DIAGNOSTIC
-		if (opt->ip6po_pktinfo || opt->ip6po_nexthop ||
-		    opt->ip6po_hbh || opt->ip6po_dest1 || opt->ip6po_dest2 ||
-		    opt->ip6po_rhinfo.ip6po_rhi_rthdr)
+	if (sopt) {
+		level = sopt->sopt_level;
+		op = sopt->sopt_dir;
+		optname = sopt->sopt_name;
+		optlen = sopt->sopt_valsize;
+	} else
+		panic("ip6_raw_ctloutput: arg soopt is NULL");
+
+	if (level != IPPROTO_IPV6) {
+		return (EINVAL);
+	}
+
+	switch (optname) {
+	case IPV6_CHECKSUM:
+		/*
+		 * For ICMPv6 sockets, no modification allowed for checksum
+		 * offset, permit "no change" values to help existing apps.
+		 *
+		 * RFC3542 says: "An attempt to set IPV6_CHECKSUM
+		 * for an ICMPv6 socket will fail."
+		 * The current behavior does not meet RFC3542.
+		 */
+		switch (op) {
+		case SOPT_SET:
+			if (optlen != sizeof(int)) {
+				error = EINVAL;
+				break;
+			}
+			error = soopt_to_kbuf(sopt, &optval,
+				    sizeof optval, sizeof optval);
+			if (error)
+				break;
+			if ((optval % 2) != 0) {
+				/* the API assumes even offset values */
+				error = EINVAL;
+			} else if (so->so_proto->pr_protocol ==
+			    IPPROTO_ICMPV6) {
+				if (optval != icmp6off)
+					error = EINVAL;
+			} else
+				in6p->in6p_cksum = optval;
+			break;
+
+		case SOPT_GET:
+			if (so->so_proto->pr_protocol == IPPROTO_ICMPV6)
+				optval = icmp6off;
+			else
+				optval = in6p->in6p_cksum;
+
+			soopt_from_kbuf(sopt, &optval, sizeof(optval));
+			break;
+
+		default:
+			error = EINVAL;
+			break;
+		}
+		break;
+
+	default:
+		error = ENOPROTOOPT;
+		break;
+	}
+
+	return (error);
+}
+
+/*
+ * Set up IP6 options in pcb for insertion in output packets or
+ * specifying behavior of outgoing packets.
+ */
+static int
+ip6_pcbopts(struct ip6_pktopts **pktopt, struct mbuf *m,
+    struct socket *so, struct sockopt *sopt)
+{
+	int priv = 0;
+	struct ip6_pktopts *opt = *pktopt;
+	int error = 0;
+
+	/* turn off any old options. */
+	if (opt) {
+#ifdef DIAGNOSTIC
+		if (opt->ip6po_pktinfo || opt->ip6po_nexthop ||
+		    opt->ip6po_hbh || opt->ip6po_dest1 || opt->ip6po_dest2 ||
+		    opt->ip6po_rhinfo.ip6po_rhi_rthdr)
 			kprintf("ip6_pcbopts: all specified options are cleared.\n");
 #endif
-		ip6_clearpktopts(opt, 1, -1);
+		ip6_clearpktopts(opt, -1);
 	} else
 		opt = kmalloc(sizeof(*opt), M_IP6OPT, M_WAITOK);
 	*pktopt = NULL;
 
-	if (m == NULL || m->m_len == 0) {
+	if (!m || m->m_len == 0) {
 		/*
 		 * Only turning off any previous options, regardless of
 		 * whether the opt is just created or given.
@@ -1722,10 +2197,8 @@
 	}
 
 	/*  set options specified by user. */
-	if (suser(td) == 0)
-		priv = 1;
-	if ((error = ip6_setpktoptions(m, opt, priv, 1)) != 0) {
-		ip6_clearpktopts(opt, 1, -1); /* XXX: discard all options */
+	if ((error = ip6_setpktoptions(m, opt, NULL, so->so_proto->pr_protocol, priv)) != 0) {
+		ip6_clearpktopts(opt, -1); /* XXX: discard all options */
 		kfree(opt, M_IP6OPT);
 		return (error);
 	}
@@ -1733,6 +2206,128 @@
 	return (0);
 }
 
+
+/*
+ * Below three functions are introduced by merge to RFC3542
+ */
+
+static int
+ip6_getpcbopt(struct ip6_pktopts *pktopt, int optname, struct sockopt *sopt)
+{
+	void *optdata = NULL;
+	int optdatalen = 0;
+	struct ip6_ext *ip6e;
+	int error = 0;
+	struct in6_pktinfo null_pktinfo;
+	int deftclass = 0, on;
+	int defminmtu = IP6PO_MINMTU_MCASTONLY;
+	int defpreftemp = IP6PO_TEMPADDR_SYSTEM;
+
+	switch (optname) {
+	case IPV6_PKTINFO:
+		if (pktopt && pktopt->ip6po_pktinfo)
+			optdata = (void *)pktopt->ip6po_pktinfo;
+		else {
+			/* XXX: we don't have to do this every time... */
+			bzero(&null_pktinfo, sizeof(null_pktinfo));
+			optdata = (void *)&null_pktinfo;
+		}
+		optdatalen = sizeof(struct in6_pktinfo);
+		break;
+	case IPV6_TCLASS:
+		if (pktopt && pktopt->ip6po_tclass >= 0)
+			optdata = (void *)&pktopt->ip6po_tclass;
+		else
+			optdata = (void *)&deftclass;
+		optdatalen = sizeof(int);
+		break;
+	case IPV6_HOPOPTS:
+		if (pktopt && pktopt->ip6po_hbh) {
+			optdata = (void *)pktopt->ip6po_hbh;
+			ip6e = (struct ip6_ext *)pktopt->ip6po_hbh;
+			optdatalen = (ip6e->ip6e_len + 1) << 3;
+		}
+		break;
+	case IPV6_RTHDR:
+		if (pktopt && pktopt->ip6po_rthdr) {
+			optdata = (void *)pktopt->ip6po_rthdr;
+			ip6e = (struct ip6_ext *)pktopt->ip6po_rthdr;
+			optdatalen = (ip6e->ip6e_len + 1) << 3;
+		}
+		break;
+	case IPV6_RTHDRDSTOPTS:
+		if (pktopt && pktopt->ip6po_dest1) {
+			optdata = (void *)pktopt->ip6po_dest1;
+			ip6e = (struct ip6_ext *)pktopt->ip6po_dest1;
+			optdatalen = (ip6e->ip6e_len + 1) << 3;
+		}
+		break;
+	case IPV6_DSTOPTS:
+		if (pktopt && pktopt->ip6po_dest2) {
+			optdata = (void *)pktopt->ip6po_dest2;
+			ip6e = (struct ip6_ext *)pktopt->ip6po_dest2;
+			optdatalen = (ip6e->ip6e_len + 1) << 3;
+		}
+		break;
+	case IPV6_NEXTHOP:
+		if (pktopt && pktopt->ip6po_nexthop) {
+			optdata = (void *)pktopt->ip6po_nexthop;
+			optdatalen = pktopt->ip6po_nexthop->sa_len;
+		}
+		break;
+	case IPV6_USE_MIN_MTU:
+		if (pktopt)
+			optdata = (void *)&pktopt->ip6po_minmtu;
+		else
+			optdata = (void *)&defminmtu;
+		optdatalen = sizeof(int);
+		break;
+	case IPV6_DONTFRAG:
+		if (pktopt && ((pktopt->ip6po_flags) & IP6PO_DONTFRAG))
+			on = 1;
+		else
+			on = 0;
+		optdata = (void *)&on;
+		optdatalen = sizeof(on);
+		break;
+	case IPV6_PREFER_TEMPADDR:
+		if (pktopt)
+			optdata = (void *)&pktopt->ip6po_prefer_tempaddr;
+		else
+			optdata = (void *)&defpreftemp;
+		optdatalen = sizeof(int);
+		break;
+	default:		/* should not happen */
+#ifdef DIAGNOSTIC
+		panic("ip6_getpcbopt: unexpected option\n");
+#endif
+		return (ENOPROTOOPT);
+	}
+
+	soopt_from_kbuf(sopt, optdata, optdatalen);
+
+	return (error);
+}
+
+/*
+ * initialize ip6_pktopts.  beware that there are non-zero default values in
+ * the struct.
+ */
+
+static int
+ip6_pcbopt(int optname, u_char *buf, int len, struct ip6_pktopts **pktopt, int uproto)
+{
+	struct ip6_pktopts *opt;
+	int priv =0;
+	if (*pktopt == NULL) {
+		*pktopt = kmalloc(sizeof(*opt), M_IP6OPT, M_WAITOK);
+		init_ip6pktopts(*pktopt);
+	}
+	opt = *pktopt;
+
+	return (ip6_setpktoption(optname, buf, len, opt, 1, 0, uproto, priv));
+}
+
 /*
  * initialize ip6_pktopts.  beware that there are non-zero default values in
  * the struct.
@@ -1743,38 +2338,47 @@
 
 	bzero(opt, sizeof(*opt));
 	opt->ip6po_hlim = -1;	/* -1 means default hop limit */
+	opt->ip6po_tclass = -1;	/* -1 means default traffic class */
+	opt->ip6po_minmtu = IP6PO_MINMTU_MCASTONLY;
+	opt->ip6po_prefer_tempaddr = IP6PO_TEMPADDR_SYSTEM;
 }
 
 void
-ip6_clearpktopts(struct ip6_pktopts *pktopt, int needfree, int optname)
+ip6_clearpktopts(struct ip6_pktopts *pktopt, int optname)
 {
 	if (pktopt == NULL)
 		return;
 
-	if (optname == -1) {
-		if (needfree && pktopt->ip6po_pktinfo)
+	if (optname == -1 || optname == IPV6_PKTINFO) {
+		if (pktopt->ip6po_pktinfo)
 			kfree(pktopt->ip6po_pktinfo, M_IP6OPT);
 		pktopt->ip6po_pktinfo = NULL;
 	}
-	if (optname == -1)
+	if (optname == -1 || optname == IPV6_HOPLIMIT)
 		pktopt->ip6po_hlim = -1;
-	if (optname == -1) {
-		if (needfree && pktopt->ip6po_nexthop)
+	if (optname == -1 || optname == IPV6_TCLASS)
+		pktopt->ip6po_tclass = -1;
+	if (optname == -1 || optname == IPV6_NEXTHOP) {
+		if (pktopt->ip6po_nextroute.ro_rt) {
+			RTFREE(pktopt->ip6po_nextroute.ro_rt);
+			pktopt->ip6po_nextroute.ro_rt = NULL;
+		}
+		if (pktopt->ip6po_nexthop)
 			kfree(pktopt->ip6po_nexthop, M_IP6OPT);
 		pktopt->ip6po_nexthop = NULL;
 	}
-	if (optname == -1) {
-		if (needfree && pktopt->ip6po_hbh)
+	if (optname == -1 || optname == IPV6_HOPOPTS) {
+		if (pktopt->ip6po_hbh)
 			kfree(pktopt->ip6po_hbh, M_IP6OPT);
 		pktopt->ip6po_hbh = NULL;
 	}
-	if (optname == -1) {
-		if (needfree && pktopt->ip6po_dest1)
+	if (optname == -1 || optname == IPV6_RTHDRDSTOPTS) {
+		if (pktopt->ip6po_dest1)
 			kfree(pktopt->ip6po_dest1, M_IP6OPT);
 		pktopt->ip6po_dest1 = NULL;
 	}
-	if (optname == -1) {
-		if (needfree && pktopt->ip6po_rhinfo.ip6po_rhi_rthdr)
+	if (optname == -1 || optname == IPV6_RTHDR) {
+		if (pktopt->ip6po_rhinfo.ip6po_rhi_rthdr)
 			kfree(pktopt->ip6po_rhinfo.ip6po_rhi_rthdr, M_IP6OPT);
 		pktopt->ip6po_rhinfo.ip6po_rhi_rthdr = NULL;
 		if (pktopt->ip6po_route.ro_rt) {
@@ -1782,8 +2386,8 @@
 			pktopt->ip6po_route.ro_rt = NULL;
 		}
 	}
-	if (optname == -1) {
-		if (needfree && pktopt->ip6po_dest2)
+	if (optname == -1 || optname == IPV6_DSTOPTS) {
+		if (pktopt->ip6po_dest2)
 			kfree(pktopt->ip6po_dest2, M_IP6OPT);
 		pktopt->ip6po_dest2 = NULL;
 	}
@@ -1847,6 +2451,45 @@
 	kfree(dst, M_IP6OPT);
 	return (NULL);
 }
+
+static int
+copypktopts(struct ip6_pktopts *dst, struct ip6_pktopts *src, int canwait)
+{
+	if (dst == NULL || src == NULL)  {
+#ifdef DIAGNOSTIC
+		kprintf("ip6_clearpktopts: invalid argument\n");
+#endif
+		return (EINVAL);
+	}
+
+	dst->ip6po_hlim = src->ip6po_hlim;
+	dst->ip6po_tclass = src->ip6po_tclass;
+	dst->ip6po_flags = src->ip6po_flags;
+	if (src->ip6po_pktinfo) {
+		dst->ip6po_pktinfo = kmalloc(sizeof(*dst->ip6po_pktinfo),
+		    M_IP6OPT, canwait);
+		if (dst->ip6po_pktinfo == NULL)
+			goto bad;
+		*dst->ip6po_pktinfo = *src->ip6po_pktinfo;
+	}
+	if (src->ip6po_nexthop) {
+		dst->ip6po_nexthop = kmalloc(src->ip6po_nexthop->sa_len,
+		    M_IP6OPT, canwait);
+		if (dst->ip6po_nexthop == NULL)
+			goto bad;
+		bcopy(src->ip6po_nexthop, dst->ip6po_nexthop,
+		    src->ip6po_nexthop->sa_len);
+	}
+	PKTOPT_EXTHDRCPY(ip6po_hbh);
+	PKTOPT_EXTHDRCPY(ip6po_dest1);
+	PKTOPT_EXTHDRCPY(ip6po_dest2);
+	PKTOPT_EXTHDRCPY(ip6po_rthdr); /* not copy the cached route */
+	return (0);
+
+  bad:
+	ip6_clearpktopts(dst, -1);
+	return (ENOBUFS);
+}
 #undef PKTOPT_EXTHDRCPY
 
 void
@@ -1855,7 +2498,7 @@
 	if (pktopt == NULL)
 		return;
 
-	ip6_clearpktopts(pktopt, 1, -1);
+	ip6_clearpktopts(pktopt, -1);
 
 	kfree(pktopt, M_IP6OPT);
 }
@@ -2205,213 +2848,448 @@
 }
 
 /*
- * Set IPv6 outgoing packet options based on advanced API.
+ * Set a particular packet option, as a sticky option or an ancillary data
+ * item.  "len" can be 0 only when it's a sticky option.
+ * We have 4 cases of combination of "sticky" and "cmsg":
+ * "sticky=0, cmsg=0": impossible
+ * "sticky=0, cmsg=1": RFC2292 or RFC3542 ancillary data
+ * "sticky=1, cmsg=0": RFC3542 socket option
+ * "sticky=1, cmsg=1": RFC2292 socket option
  */
-int
-ip6_setpktoptions(struct mbuf *control, struct ip6_pktopts *opt, int priv,
-		  int needcopy)
+static int
+ip6_setpktoption(int optname, u_char *buf, int len, struct ip6_pktopts *opt,
+     int sticky, int cmsg, int uproto, int priv)
 {
-	struct cmsghdr *cm = NULL;
+	int minmtupolicy, preftemp;
+	//int error;
 
-	if (control == NULL || opt == NULL)
+	if (!sticky && !cmsg) {
+		kprintf("ip6_setpktoption: impossible case\n");
 		return (EINVAL);
-
-	init_ip6pktopts(opt);
+	}
 
 	/*
-	 * XXX: Currently, we assume all the optional information is stored
-	 * in a single mbuf.
-	 */
-	if (control->m_next)
-		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:
+		case IPV6_NEXTHOP:
+		case IPV6_HOPOPTS:
+		case IPV6_DSTOPTS:
+		case IPV6_RTHDRDSTOPTS:
+		case IPV6_RTHDR:
+		case IPV6_USE_MIN_MTU:
+		case IPV6_DONTFRAG:
+		case IPV6_TCLASS:
+		case IPV6_PREFER_TEMPADDR: /* XXX: not an RFC3542 option */
+			return (ENOPROTOOPT);
+		}
+	}
 
-	for (; control->m_len; control->m_data += CMSG_ALIGN(cm->cmsg_len),
-		     control->m_len -= CMSG_ALIGN(cm->cmsg_len)) {
-		cm = mtod(control, struct cmsghdr *);
-		if (cm->cmsg_len == 0 || cm->cmsg_len > control->m_len)
+	switch (optname) {
+	case IPV6_2292PKTINFO:
+	case IPV6_PKTINFO:
+	{
+		struct in6_pktinfo *pktinfo;
+		if (len != sizeof(struct in6_pktinfo))
 			return (EINVAL);
-		if (cm->cmsg_level != IPPROTO_IPV6)
-			continue;
+		pktinfo = (struct in6_pktinfo *)buf;
 
 		/*
-		 * XXX should check if RFC2292 API is mixed with 2292bis API
+		 * An application can clear any sticky IPV6_PKTINFO option by
+		 * doing a "regular" setsockopt with ipi6_addr being
+		 * in6addr_any and ipi6_ifindex being zero.
+		 * [RFC 3542, Section 6]
 		 */
-		switch (cm->cmsg_type) {
-		case IPV6_PKTINFO:
-			if (cm->cmsg_len != CMSG_LEN(sizeof(struct in6_pktinfo)))
-				return (EINVAL);
-			if (needcopy) {
-				/* XXX: Is it really WAITOK? */
-				opt->ip6po_pktinfo =
-					kmalloc(sizeof(struct in6_pktinfo),
-					       M_IP6OPT, M_WAITOK);
-				bcopy(CMSG_DATA(cm), opt->ip6po_pktinfo,
-				    sizeof(struct in6_pktinfo));
-			} else
-				opt->ip6po_pktinfo =
-					(struct in6_pktinfo *)CMSG_DATA(cm);
-			if (opt->ip6po_pktinfo->ipi6_ifindex &&
-			    IN6_IS_ADDR_LINKLOCAL(&opt->ip6po_pktinfo->ipi6_addr))
-				opt->ip6po_pktinfo->ipi6_addr.s6_addr16[1] =
-					htons(opt->ip6po_pktinfo->ipi6_ifindex);
-
-			if (opt->ip6po_pktinfo->ipi6_ifindex > if_index
-			 || opt->ip6po_pktinfo->ipi6_ifindex < 0) {
-				return (ENXIO);
-			}
-
-			/*
-			 * Check if the requested source address is indeed a
-			 * unicast address assigned to the node, and can be
-			 * used as the packet's source address.
-			 */
-			if (!IN6_IS_ADDR_UNSPECIFIED(&opt->ip6po_pktinfo->ipi6_addr)) {
-				struct in6_ifaddr *ia6;
-				struct sockaddr_in6 sin6;
-
-				bzero(&sin6, sizeof(sin6));
-				sin6.sin6_len = sizeof(sin6);
-				sin6.sin6_family = AF_INET6;
-				sin6.sin6_addr =
-					opt->ip6po_pktinfo->ipi6_addr;
-				ia6 = (struct in6_ifaddr *)ifa_ifwithaddr(sin6tosa(&sin6));
-				if (ia6 == NULL ||
-				    (ia6->ia6_flags & (IN6_IFF_ANYCAST |
-						       IN6_IFF_NOTREADY)) != 0)
-					return (EADDRNOTAVAIL);
-			}
+		if (optname == IPV6_PKTINFO && opt->ip6po_pktinfo &&
+		    pktinfo->ipi6_ifindex == 0 &&
+		    IN6_IS_ADDR_UNSPECIFIED(&pktinfo->ipi6_addr)) {
+			ip6_clearpktopts(opt, optname);
 			break;
+		}
 
-		case IPV6_HOPLIMIT:
-			if (cm->cmsg_len != CMSG_LEN(sizeof(int)))
-				return (EINVAL);
+		if (uproto == IPPROTO_TCP && optname == IPV6_PKTINFO &&
+		    sticky && !IN6_IS_ADDR_UNSPECIFIED(&pktinfo->ipi6_addr)) {
+			return (EINVAL);
+		}
 
-			opt->ip6po_hlim = *(int *)CMSG_DATA(cm);
-			if (opt->ip6po_hlim < -1 || opt->ip6po_hlim > 255)
-				return (EINVAL);
-			break;
+		/* validate the interface index if specified. */
+		if (pktinfo->ipi6_ifindex > if_index ||
+		    pktinfo->ipi6_ifindex < 0) {
+			 return (ENXIO);
+		}
+		/*
+			* Check if the requested source address is indeed a
+			* unicast address assigned to the node, and can be
+			* used as the packet's source address.
+		*/
+		if (!IN6_IS_ADDR_UNSPECIFIED(&opt->ip6po_pktinfo->ipi6_addr)) {
+			struct in6_ifaddr *ia6;
+			struct sockaddr_in6 sin6;
+
+			bzero(&sin6, sizeof(sin6));
+			sin6.sin6_len = sizeof(sin6);
+			sin6.sin6_family = AF_INET6;
+			sin6.sin6_addr =
+			opt->ip6po_pktinfo->ipi6_addr;
+			ia6 = (struct in6_ifaddr *)ifa_ifwithaddr(sin6tosa(&sin6));
+			if (ia6 == NULL ||
+				(ia6->ia6_flags & (IN6_IFF_ANYCAST |
+					IN6_IFF_NOTREADY)) != 0)
+			return (EADDRNOTAVAIL);
+		}
 
-		case IPV6_NEXTHOP:
-			if (!priv)
-				return (EPERM);
+		/*
+		 * We store the address anyway, and let in6_selectsrc()
+		 * validate the specified address.  This is because ipi6_addr
+		 * may not have enough information about its scope zone, and
+		 * we may need additional information (such as outgoing
+		 * interface or the scope zone of a destination address) to
+		 * disambiguate the scope.
+		 * XXX: the delay of the validation may confuse the
+		 * application when it is used as a sticky option.
+		 */
+		if (opt->ip6po_pktinfo == NULL) {
+			opt->ip6po_pktinfo = kmalloc(sizeof(*pktinfo),
+			    M_IP6OPT, M_NOWAIT);
+			if (opt->ip6po_pktinfo == NULL)
+				return (ENOBUFS);
+		}
+		bcopy(pktinfo, opt->ip6po_pktinfo, sizeof(*pktinfo));
+		break;
+	}
 
-			if (cm->cmsg_len < sizeof(u_char) ||
-			    /* check if cmsg_len is large enough for sa_len */
-			    cm->cmsg_len < CMSG_LEN(*CMSG_DATA(cm)))
-				return (EINVAL);
+	case IPV6_2292HOPLIMIT:
+	case IPV6_HOPLIMIT:
+	{
+		int *hlimp;
 
-			if (needcopy) {
-				opt->ip6po_nexthop =
-					kmalloc(*CMSG_DATA(cm),
-					       M_IP6OPT, M_WAITOK);
-				bcopy(CMSG_DATA(cm),
-				      opt->ip6po_nexthop,
-				      *CMSG_DATA(cm));
-			} else
-				opt->ip6po_nexthop =
-					(struct sockaddr *)CMSG_DATA(cm);
-			break;
+		/*
+		 * RFC 3542 deprecated the usage of sticky IPV6_HOPLIMIT
+		 * to simplify the ordering among hoplimit options.
+		 */
+		if (optname == IPV6_HOPLIMIT && sticky)
+			return (ENOPROTOOPT);
 
-		case IPV6_HOPOPTS:
-		{
-			struct ip6_hbh *hbh;
-			int hbhlen;
+		if (len != sizeof(int))
+			return (EINVAL);
+		hlimp = (int *)buf;
+		if (*hlimp < -1 || *hlimp > 255)
+			return (EINVAL);
 
-			if (cm->cmsg_len < CMSG_LEN(sizeof(struct ip6_hbh)))
-				return (EINVAL);
-			hbh = (struct ip6_hbh *)CMSG_DATA(cm);
-			hbhlen = (hbh->ip6h_len + 1) << 3;
-			if (cm->cmsg_len != CMSG_LEN(hbhlen))
-				return (EINVAL);
+		opt->ip6po_hlim = *hlimp;
+		break;
+	}
 
-			if (needcopy) {
-				opt->ip6po_hbh =
-					kmalloc(hbhlen, M_IP6OPT, M_WAITOK);
-				bcopy(hbh, opt->ip6po_hbh, hbhlen);
-			} else
-				opt->ip6po_hbh = hbh;
+	case IPV6_TCLASS:
+	{
+		int tclass;
+
+		if (len != sizeof(int))
+			return (EINVAL);
+		tclass = *(int *)buf;
+		if (tclass < -1 || tclass > 255)
+			return (EINVAL);
+
+		opt->ip6po_tclass = tclass;
+		break;
+	}
+
+	case IPV6_2292NEXTHOP:
+	case IPV6_NEXTHOP:
+		if (!priv)
+			return (EPERM);
+
+		if (len == 0) {	/* just remove the option */
+			ip6_clearpktopts(opt, IPV6_NEXTHOP);
 			break;
 		}
 
-		case IPV6_DSTOPTS:
+		/* check if cmsg_len is large enough for sa_len */
+		if (len < sizeof(struct sockaddr) || len < *buf)
+			return (EINVAL);
+
+		switch (((struct sockaddr *)buf)->sa_family) {
+		case AF_INET6:
 		{
-			struct ip6_dest *dest, **newdest;
-			int destlen;
+			struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)buf;
+			//int error;
 
-			if (cm->cmsg_len < CMSG_LEN(sizeof(struct ip6_dest)))
+			if (sa6->sin6_len != sizeof(struct sockaddr_in6))
 				return (EINVAL);
-			dest = (struct ip6_dest *)CMSG_DATA(cm);
-			destlen = (dest->ip6d_len + 1) << 3;
-			if (cm->cmsg_len != CMSG_LEN(destlen))
+
+			if (IN6_IS_ADDR_UNSPECIFIED(&sa6->sin6_addr) ||
+			    IN6_IS_ADDR_MULTICAST(&sa6->sin6_addr)) {
 				return (EINVAL);
+			}
+			break;
+		}
+		case AF_LINK:	/* should eventually be supported */
+		default:
+			return (EAFNOSUPPORT);
+		}
+
+		/* turn off the previous option, then set the new option. */
+		ip6_clearpktopts(opt, IPV6_NEXTHOP);
+		opt->ip6po_nexthop = kmalloc(*buf, M_IP6OPT, M_NOWAIT);
+		if (opt->ip6po_nexthop == NULL)
+			return (ENOBUFS);
+		bcopy(buf, opt->ip6po_nexthop, *buf);
+		break;
+
+	case IPV6_2292HOPOPTS:
+	case IPV6_HOPOPTS:
+	{
+		struct ip6_hbh *hbh;
+		int hbhlen;
+
+		/*
+		 * XXX: We don't allow a non-privileged user to set ANY HbH
+		 * options, since per-option restriction has too much
+		 * overhead.
+		 */
+		if (!priv)
+			return (EPERM);
+		if (len == 0) {
+			ip6_clearpktopts(opt, IPV6_HOPOPTS);
+			break;	/* just remove the option */
+		}
 
+		/* message length validation */
+		if (len < sizeof(struct ip6_hbh))
+			return (EINVAL);
+		hbh = (struct ip6_hbh *)buf;
+		hbhlen = (hbh->ip6h_len + 1) << 3;
+		if (len != hbhlen)
+			return (EINVAL);
+
+		/* turn off the previous option, then set the new option. */
+		ip6_clearpktopts(opt, IPV6_HOPOPTS);
+		opt->ip6po_hbh = kmalloc(hbhlen, M_IP6OPT, M_NOWAIT);
+		if (opt->ip6po_hbh == NULL)
+			return (ENOBUFS);
+		bcopy(hbh, opt->ip6po_hbh, hbhlen);
+
+		break;
+	}
+
+	case IPV6_2292DSTOPTS:
+	case IPV6_DSTOPTS:
+	case IPV6_RTHDRDSTOPTS:
+	{
+		struct ip6_dest *dest, **newdest = NULL;
+		int destlen;
+		if (!priv)
+			return (EPERM);
+
+		if (len == 0) {
+			ip6_clearpktopts(opt, optname);
+			break;	/* just remove the option */
+		}
+
+		/* message length validation */
+		if (len < sizeof(struct ip6_dest))
+			return (EINVAL);
+		dest = (struct ip6_dest *)buf;
+		destlen = (dest->ip6d_len + 1) << 3;
+		if (len != destlen)
+			return (EINVAL);
+
+		/*
+		 * Determine the position that the destination options header
+		 * should be inserted; before or after the routing header.
+		 */
+		switch (optname) {
+		case IPV6_2292DSTOPTS:
 			/*
-			 * The old advacned API is ambiguous on this
-			 * point. Our approach is to determine the
-			 * position based according to the existence
-			 * of a routing header. Note, however, that
-			 * this depends on the order of the extension
-			 * headers in the ancillary data; the 1st part
-			 * of the destination options header must
-			 * appear before the routing header in the
-			 * ancillary data, too.
-			 * RFC2292bis solved the ambiguity by
-			 * introducing separate cmsg types.
+			 * The old advacned API is ambiguous on this point.
+			 * Our approach is to determine the position based
+			 * according to the existence of a routing header.
+			 * Note, however, that this depends on the order of the
+			 * extension headers in the ancillary data; the 1st
+			 * part of the destination options header must appear
+			 * before the routing header in the ancillary data,
+			 * too.
+			 * RFC3542 solved the ambiguity by introducing
+			 * separate ancillary data or option types.
 			 */
 			if (opt->ip6po_rthdr == NULL)
 				newdest = &opt->ip6po_dest1;
 			else
 				newdest = &opt->ip6po_dest2;
+			break;
+		case IPV6_RTHDRDSTOPTS:
+			newdest = &opt->ip6po_dest1;
+			break;
+		case IPV6_DSTOPTS:
+			newdest = &opt->ip6po_dest2;
+			break;
+		}
 
-			if (needcopy) {
-				*newdest = kmalloc(destlen, M_IP6OPT, M_WAITOK);
-				bcopy(dest, *newdest, destlen);
-			} else
-				*newdest = dest;
+		/* turn off the previous option, then set the new option. */
+		ip6_clearpktopts(opt, optname);
+		*newdest = kmalloc(destlen, M_IP6OPT, M_NOWAIT);
+		if (*newdest == NULL)
+			return (ENOBUFS);
+		bcopy(dest, *newdest, destlen);
 
-			break;
+		break;
+	}
+
+	case IPV6_2292RTHDR:
+	case IPV6_RTHDR:
+	{
+		struct ip6_rthdr *rth;
+		int rthlen;
+
+		if (len == 0) {
+			ip6_clearpktopts(opt, IPV6_RTHDR);
+			break;	/* just remove the option */
 		}
 
-		case IPV6_RTHDR:
-		{
-			struct ip6_rthdr *rth;
-			int rthlen;
+		/* message length validation */
+		if (len < sizeof(struct ip6_rthdr))
+			return (EINVAL);
+		rth = (struct ip6_rthdr *)buf;
+		rthlen = (rth->ip6r_len + 1) << 3;
+		if (len != rthlen)
+			return (EINVAL);
 
-			if (cm->cmsg_len < CMSG_LEN(sizeof(struct ip6_rthdr)))
-				return (EINVAL);
-			rth = (struct ip6_rthdr *)CMSG_DATA(cm);
-			rthlen = (rth->ip6r_len + 1) << 3;
-			if (cm->cmsg_len != CMSG_LEN(rthlen))
-				return (EINVAL);
+		switch (rth->ip6r_type) {
+		default:
+			return (EINVAL);	/* not supported */
+		}
 
-			switch (rth->ip6r_type) {
-			case IPV6_RTHDR_TYPE_0:
-				/* must contain one addr */
-				if (rth->ip6r_len == 0)
-					return (EINVAL);
-				/* length must be even */
-				if (rth->ip6r_len % 2)
-					return (EINVAL);
-				if (rth->ip6r_len / 2 != rth->ip6r_segleft)
-					return (EINVAL);
-				break;
-			default:
-				return (EINVAL);	/* not supported */
-			}
+		/* turn off the previous option */
+		ip6_clearpktopts(opt, IPV6_RTHDR);
+		opt->ip6po_rthdr = kmalloc(rthlen, M_IP6OPT, M_NOWAIT);
+		if (opt->ip6po_rthdr == NULL)
+			return (ENOBUFS);
+		bcopy(rth, opt->ip6po_rthdr, rthlen);
 
-			if (needcopy) {
-				opt->ip6po_rthdr = kmalloc(rthlen, M_IP6OPT,
-							  M_WAITOK);
-				bcopy(rth, opt->ip6po_rthdr, rthlen);
-			} else
-				opt->ip6po_rthdr = rth;
+		break;
+	}
 
-			break;
+	case IPV6_USE_MIN_MTU:
+		if (len != sizeof(int))
+			return (EINVAL);
+		minmtupolicy = *(int *)buf;
+		if (minmtupolicy != IP6PO_MINMTU_MCASTONLY &&
+		    minmtupolicy != IP6PO_MINMTU_DISABLE &&
+		    minmtupolicy != IP6PO_MINMTU_ALL) {
+			return (EINVAL);
 		}
+		opt->ip6po_minmtu = minmtupolicy;
+		break;
 
-		default:
-			return (ENOPROTOOPT);
+	case IPV6_DONTFRAG:
+		if (len != sizeof(int))
+			return (EINVAL);
+
+		if (uproto == IPPROTO_TCP || *(int *)buf == 0) {
+			/*
+			 * we ignore this option for TCP sockets.
+			 * (RFC3542 leaves this case unspecified.)
+			 */
+			opt->ip6po_flags &= ~IP6PO_DONTFRAG;
+		} else
+			opt->ip6po_flags |= IP6PO_DONTFRAG;
+		break;
+
+	case IPV6_PREFER_TEMPADDR:
+		if (len != sizeof(int))
+			return (EINVAL);
+		preftemp = *(int *)buf;
+		if (preftemp != IP6PO_TEMPADDR_SYSTEM &&
+		    preftemp != IP6PO_TEMPADDR_NOTPREFER &&
+		    preftemp != IP6PO_TEMPADDR_PREFER) {
+			return (EINVAL);
 		}
+		opt->ip6po_prefer_tempaddr = preftemp;
+		break;
+
+	default:
+		return (ENOPROTOOPT);
+	} /* end of switch */
+
+	return (0);
+}
+
+
+/*
+ * Set IPv6 outgoing packet options based on advanced API.
+ */
+int
+ip6_setpktoptions(struct mbuf *control, struct ip6_pktopts *opt,
+    struct ip6_pktopts *stickyopt, int uproto, int priv)
+{
+	struct cmsghdr *cm = NULL;
+
+	if (control == NULL || opt == NULL)
+		return (EINVAL);
+
+	init_ip6pktopts(opt);
+
+	/*
+	 * XXX: Currently, we assume all the optional information is stored
+	 * in a single mbuf.
+	 */
+	if (stickyopt) {
+		int error;
+
+		/*
+		 * If stickyopt is provided, make a local copy of the options
+		 * for this particular packet, then override them by ancillary
+		 * objects.
+		 * XXX: copypktopts() does not copy the cached route to a next
+		 * hop (if any).  This is not very good in terms of efficiency,
+		 * but we can allow this since this option should be rarely
+		 * used.
+		 */
+		if ((error = copypktopts(opt, stickyopt, M_NOWAIT)) != 0)
+			return (error);
+	}
+
+	/*
+	 * XXX: Currently, we assume all the optional information is stored
+	 * in a single mbuf.
+	 */
+	if (control->m_next)
+		return (EINVAL);
+
+	for (; control->m_len; control->m_data += CMSG_ALIGN(cm->cmsg_len),
+	    control->m_len -= CMSG_ALIGN(cm->cmsg_len)) {
+		int error;
+
+		if (control->m_len < CMSG_LEN(0))
+			return (EINVAL);
+
+		cm = mtod(control, struct cmsghdr *);
+		if (cm->cmsg_len == 0 || cm->cmsg_len > control->m_len)
+			return (EINVAL);
+		if (cm->cmsg_level != IPPROTO_IPV6)
+			continue;
+
+		error = ip6_setpktoption(cm->cmsg_type, CMSG_DATA(cm),
+		    cm->cmsg_len - CMSG_LEN(0), opt, 0, 1, uproto, priv);
+		if (error)
+			return (error);
 	}
 
 	return (0);
Index: ./sys/netinet6/ip6_var.h
===================================================================
RCS file: /home/dcvs/src/sys/netinet6/ip6_var.h,v
retrieving revision 1.12
diff -u -r1.12 ip6_var.h
--- ./sys/netinet6/ip6_var.h	7 May 2007 12:40:30 -0000	1.12
+++ ./sys/netinet6/ip6_var.h	22 Aug 2008 07:42:30 -0000
@@ -138,6 +138,14 @@
 #define ip6po_rthdr	ip6po_rhinfo.ip6po_rhi_rthdr
 #define ip6po_route	ip6po_rhinfo.ip6po_rhi_route
 
+/* Nexthop related info */
+struct	ip6po_nhinfo {
+	struct	sockaddr *ip6po_nhi_nexthop;
+	struct	route_in6 ip6po_nhi_route; /* Route to the nexthop */
+};
+#define ip6po_nexthop	ip6po_nhinfo.ip6po_nhi_nexthop
+#define ip6po_nextroute	ip6po_nhinfo.ip6po_nhi_route
+
 struct	ip6_pktopts {
 	struct	mbuf *ip6po_m;	/* Pointer to mbuf storing the data */
 	int	ip6po_hlim;	/* Hoplimit for outgoing packets */
@@ -145,7 +153,8 @@
 	/* Outgoing IF/address information */
 	struct	in6_pktinfo *ip6po_pktinfo;
 
-	struct	sockaddr *ip6po_nexthop; /* Next-hop address */
+	/* Next-hop address information */
+	struct	ip6po_nhinfo ip6po_nhinfo;
 	
 	struct	ip6_hbh *ip6po_hbh; /* Hop-by-Hop options header */
 
@@ -157,12 +166,32 @@
 
 	/* Destination options header (after a routing header) */
 	struct	ip6_dest *ip6po_dest2;
+	/*
+	 * below fields are introduced in RFC3542
+	 */
+	int	ip6po_tclass;	/* traffic class */
+	int	ip6po_minmtu;  /* fragment vs PMTU discovery policy */
+#define IP6PO_MINMTU_MCASTONLY	-1 /* default; send at min MTU for multicast*/
+#define IP6PO_MINMTU_DISABLE	 0 /* always perform pmtu disc */
+#define IP6PO_MINMTU_ALL	 1 /* always send at min MTU */
+	int	ip6po_prefer_tempaddr;  /* whether temporary addresses are
+					   preferred as source address */
+#define IP6PO_TEMPADDR_SYSTEM	-1 /* follow the system default */
+#define IP6PO_TEMPADDR_NOTPREFER 0 /* not prefer temporary address */
+#define IP6PO_TEMPADDR_PREFER	 1 /* prefer temporary address */
+
+	int ip6po_flags;
+#if 0	/* parameters in this block is obsolete. do not reuse the values. */
+#define IP6PO_REACHCONF	0x01	/* upper-layer reachability confirmation. */
+#define IP6PO_MINMTU	0x02	/* use minimum MTU (IPV6_USE_MIN_MTU) */
+#endif
+#define IP6PO_DONTFRAG	0x04	/* disable fragmentation (IPV6_DONTFRAG) */
+#define IP6PO_USECOA	0x08	/* use care of address */
 };
 
 /*
  * Control options for incoming packets
  */
-
 struct	ip6stat {
 	u_quad_t ip6s_total;		/* total packets received */
 	u_quad_t ip6s_tooshort;		/* packet too short */
@@ -341,9 +370,12 @@
 			struct ip6_moptions *, struct ifnet **,
 			struct inpcb *);
 int	ip6_ctloutput (struct socket *, struct sockopt *sopt);
+int	ip6_raw_ctloutput (struct socket *, struct sockopt *);
 void	init_ip6pktopts (struct ip6_pktopts *);
-int	ip6_setpktoptions (struct mbuf *, struct ip6_pktopts *, int, int);
-void	ip6_clearpktopts (struct ip6_pktopts *, int, int);
+int
+ip6_setpktoptions(struct mbuf *, struct ip6_pktopts *,
+    struct ip6_pktopts *, int , int);
+void	ip6_clearpktopts (struct ip6_pktopts *, int);
 struct ip6_pktopts *ip6_copypktopts (struct ip6_pktopts *, int);
 int	ip6_optlen (struct inpcb *);
 
Index: ./sys/netinet6/raw_ip6.c
===================================================================
RCS file: /home/dcvs/src/sys/netinet6/raw_ip6.c,v
retrieving revision 1.26
diff -u -r1.26 raw_ip6.c
--- ./sys/netinet6/raw_ip6.c	17 May 2008 20:33:36 -0000	1.26
+++ ./sys/netinet6/raw_ip6.c	13 Jul 2008 05:44:52 -0000
@@ -338,7 +338,9 @@
 		priv = 1;
 	dst = &dstsock->sin6_addr;
 	if (control) {
-		if ((error = ip6_setpktoptions(control, &opt, priv, 0)) != 0)
+		if ((error = ip6_setpktoptions(control, &opt,
+		    in6p->in6p_outputopts, 
+		    so->so_proto->pr_protocol, priv)) != 0)
 			goto bad;
 		optp = &opt;
 	} else
@@ -477,7 +479,7 @@
 		RTFREE(optp->ip6po_route.ro_rt);
 	if (control) {
 		if (optp == &opt)
-			ip6_clearpktopts(optp, 0, -1);
+			ip6_clearpktopts(optp, -1);
 		m_freem(control);
 	}
 	return (error);
@@ -514,6 +516,9 @@
 		case MRT6_PIM:
 			error = ip6_mrouter_get(so, sopt);
 			break;
+		case IPV6_CHECKSUM:
+			error = ip6_raw_ctloutput(so, sopt);
+			break;
 		default:
 			error = ip6_ctloutput(so, sopt);
 			break;
@@ -531,6 +536,9 @@
 		case MRT6_PIM:
 			error = ip6_mrouter_set(so, sopt);
 			break;
+		case IPV6_CHECKSUM:
+			error = ip6_raw_ctloutput(so, sopt);
+			break;
 		default:
 			error = ip6_ctloutput(so, sopt);
 			break;
Index: ./sys/netinet6/udp6_output.c
===================================================================
RCS file: /home/dcvs/src/sys/netinet6/udp6_output.c,v
retrieving revision 1.8
diff -u -r1.8 udp6_output.c
--- ./sys/netinet6/udp6_output.c	29 Dec 2006 18:02:56 -0000	1.8
+++ ./sys/netinet6/udp6_output.c	22 Jun 2008 02:48:12 -0000
@@ -139,7 +139,9 @@
 
 	priv = !suser(td);	/* 1 if privilaged, 0 if not */
 	if (control) {
-		if ((error = ip6_setpktoptions(control, &opt, priv, 0)) != 0)
+		if ((error = ip6_setpktoptions(control, &opt,
+		    in6p->in6p_outputopts, 
+		    IPPROTO_UDP, priv)) != 0)
 			goto release;
 		in6p->in6p_outputopts = &opt;
 	}
@@ -304,7 +306,7 @@
 
 releaseopt:
 	if (control) {
-		ip6_clearpktopts(in6p->in6p_outputopts, 0, -1);
+		ip6_clearpktopts(in6p->in6p_outputopts, -1);
 		in6p->in6p_outputopts = stickyopt;
 		m_freem(control);
 	}
Index: ./sys/netinet6/ip6_input.c
===================================================================
RCS file: /home/dcvs/src/sys/netinet6/ip6_input.c,v
retrieving revision 1.35
diff -u -r1.35 ip6_input.c
--- ./sys/netinet6/ip6_input.c	14 Oct 2007 18:15:19 -0000	1.35
+++ ./sys/netinet6/ip6_input.c	22 Aug 2008 07:39:22 -0000
@@ -1119,10 +1119,15 @@
  * called IP6_EXTHDR_CHECK() and all the extension headers are located in the
  * very first mbuf on the mbuf chain.
  */
+
+/*
+ * this function has been updated during the merge from RFC2292 to RFC3542
+ */
 void
 ip6_savecontrol(struct inpcb *in6p, struct mbuf **mp, struct ip6_hdr *ip6,
 		struct mbuf *m)
 {
+	#define IS2292(x, y)	((in6p->in6p_flags & IN6P_RFC2292) ? (x) : (y))
 	struct thread *td = curthread;	/* XXX */
 	int privileged = 0;
 	int rthdr_exist = 0;
@@ -1154,7 +1159,7 @@
 					? m->m_pkthdr.rcvif->if_index
 					: 0;
 		*mp = sbcreatecontrol((caddr_t) &pi6,
-			sizeof(struct in6_pktinfo), IPV6_PKTINFO,
+			sizeof(struct in6_pktinfo), IS2292(IPV6_2292PKTINFO, IPV6_PKTINFO),
 			IPPROTO_IPV6);
 		if (*mp)
 			mp = &(*mp)->m_next;
@@ -1163,7 +1168,21 @@
 	if (in6p->in6p_flags & IN6P_HOPLIMIT) {
 		int hlim = ip6->ip6_hlim & 0xff;
 		*mp = sbcreatecontrol((caddr_t) &hlim,
-			sizeof(int), IPV6_HOPLIMIT, IPPROTO_IPV6);
+			sizeof(int), IS2292(IPV6_2292HOPLIMIT, IPV6_HOPLIMIT), IPPROTO_IPV6);
+		if (*mp)
+			mp = &(*mp)->m_next;
+	}
+
+	if ((in6p->in6p_flags & IN6P_TCLASS) != 0) {
+		u_int32_t flowinfo;
+		int tclass;
+
+		flowinfo = (u_int32_t)ntohl(ip6->ip6_flow & IPV6_FLOWINFO_MASK);
+		flowinfo >>= 20;
+
+		tclass = flowinfo & 0xff;
+		*mp = sbcreatecontrol((caddr_t) &tclass, sizeof(tclass),
+		    IPV6_TCLASS, IPPROTO_IPV6);
 		if (*mp)
 			mp = &(*mp)->m_next;
 	}
@@ -1216,7 +1235,7 @@
 			 * Note: this constraint is removed in 2292bis.
 			 */
 			*mp = sbcreatecontrol((caddr_t)hbh, hbhlen,
-					      IPV6_HOPOPTS, IPPROTO_IPV6);
+				 IS2292(IPV6_2292HOPOPTS, IPV6_HOPOPTS), IPPROTO_IPV6);
 			if (*mp)
 				mp = &(*mp)->m_next;
 #ifdef PULLDOWN_TEST
@@ -1331,7 +1350,7 @@
 					break;
 
 				*mp = sbcreatecontrol((caddr_t)ip6e, elen,
-						      IPV6_DSTOPTS,
+						       IS2292(IPV6_2292DSTOPTS, IPV6_DSTOPTS),
 						      IPPROTO_IPV6);
 				if (*mp)
 					mp = &(*mp)->m_next;
@@ -1341,7 +1360,7 @@
 					break;
 
 				*mp = sbcreatecontrol((caddr_t)ip6e, elen,
-						      IPV6_RTHDR,
+						      IS2292(IPV6_2292RTHDR, IPV6_RTHDR),
 						      IPPROTO_IPV6);
 				if (*mp)
 					mp = &(*mp)->m_next;
@@ -1376,7 +1395,42 @@
 	  loopend:
 		;
 	}
+#undef IS2292
+}
+
+void
+ip6_notify_pmtu(struct inpcb *in6p, struct sockaddr_in6 *dst, u_int32_t *mtu)
+{
+	struct socket *so;
+	struct mbuf *m_mtu;
+	struct ip6_mtuinfo mtuctl;
+
+	so =  in6p->inp_socket;
+
+	if (mtu == NULL)
+		return;
+
+#ifdef DIAGNOSTIC
+	if (so == NULL)		/* I believe this is impossible */
+		panic("ip6_notify_pmtu: socket is NULL");
+#endif
+
+	bzero(&mtuctl, sizeof(mtuctl));	/* zero-clear for safety */
+	mtuctl.ip6m_mtu = *mtu;
+	mtuctl.ip6m_addr = *dst;
+
+	if ((m_mtu = sbcreatecontrol((caddr_t)&mtuctl, sizeof(mtuctl),
+	    IPV6_PATHMTU, IPPROTO_IPV6)) == NULL)
+		return;
+
+	if (sbappendaddr(&so->so_rcv, (struct sockaddr *)dst, NULL, m_mtu)
+	    == 0) {
+		m_freem(m_mtu);
+		/* XXX: should count statistics */
+	} else
+		sorwakeup(so);
 
+	return;
 }
 
 #ifdef PULLDOWN_TEST
Index: ./sys/netinet6/route6.c
===================================================================
RCS file: /home/dcvs/src/sys/netinet6/route6.c,v
retrieving revision 1.9
diff -u -r1.9 route6.c
--- ./sys/netinet6/route6.c	7 May 2007 13:00:16 -0000	1.9
+++ ./sys/netinet6/route6.c	22 Aug 2008 06:57:02 -0000
@@ -49,11 +49,6 @@
 
 #include <netinet/icmp6.h>
 
-#if 0
-static int ip6_rthdr0 (struct mbuf *, struct ip6_hdr *,
-    struct ip6_rthdr0 *);
-#endif
-
 int
 route6_input(struct mbuf **mp, int *offp, int proto) /* proto is unused */
 {
@@ -76,50 +71,6 @@
 #endif
 
 	switch (rh->ip6r_type) {
-#if 0
-	/*
-	 * See http://www.secdev.org/conf/IPv6_RH_security-csw07.pdf
-	 * for why IPV6_RTHDR_TYPE_0 is baned here.
-	 *
-	 * We return ICMPv6 parameter problem so that innocent people
-	 * (not an attacker) would notice about the use of IPV6_RTHDR_TYPE_0.
-	 * Since there's no amplification, and ICMPv6 error will be rate-
-	 * controlled, it shouldn't cause any problem.
-	 * If you are concerned about this, you may want to use the following
-	 * code fragment:
-	 *
-	 * case IPV6_RTHDR_TYPE_0:
-	 *	m_freem(m);
-	 *	return (IPPROTO_DONE);
-	 */
-	case IPV6_RTHDR_TYPE_0:
-		rhlen = (rh->ip6r_len + 1) << 3;
-#ifndef PULLDOWN_TEST
-		/*
-		 * note on option length:
-		 * due to IP6_EXTHDR_CHECK assumption, we cannot handle
-		 * very big routing header (max rhlen == 2048).
-		 */
-		IP6_EXTHDR_CHECK(m, off, rhlen, IPPROTO_DONE);
-#else
-		/*
-		 * note on option length:
-		 * maximum rhlen: 2048
-		 * max mbuf m_pulldown can handle: MCLBYTES == usually 2048
-		 * so, here we are assuming that m_pulldown can handle
-		 * rhlen == 2048 case.  this may not be a good thing to
-		 * assume - we may want to avoid pulling it up altogether.
-		 */
-		IP6_EXTHDR_GET(rh, struct ip6_rthdr *, m, off, rhlen);
-		if (rh == NULL) {
-			ip6stat.ip6s_tooshort++;
-			return IPPROTO_DONE;
-		}
-#endif
-		if (ip6_rthdr0(m, ip6, (struct ip6_rthdr0 *)rh))
-			return (IPPROTO_DONE);
-		break;
-#endif
 	default:
 		/* unknown routing type */
 		if (rh->ip6r_segleft == 0) {
@@ -135,93 +86,3 @@
 	*offp += rhlen;
 	return (rh->ip6r_nxt);
 }
-
-#if 0
-/*
- * Type0 routing header processing
- *
- * RFC2292 backward compatibility warning: no support for strict/loose bitmap,
- * as it was dropped between RFC1883 and RFC2460.
- */
-static int
-ip6_rthdr0(struct mbuf *m, struct ip6_hdr *ip6, struct ip6_rthdr0 *rh0)
-{
-	int addrs, index;
-	struct in6_addr *nextaddr, tmpaddr;
-
-	if (rh0->ip6r0_segleft == 0)
-		return (0);
-
-	if (rh0->ip6r0_len % 2
-#ifdef COMPAT_RFC1883
-	    || rh0->ip6r0_len > 46
-#endif
-		) {
-		/*
-		 * Type 0 routing header can't contain more than 23 addresses.
-		 * RFC 2462: this limitation was removed since strict/loose
-		 * bitmap field was deleted.
-		 */
-		ip6stat.ip6s_badoptions++;
-		icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER,
-			    (caddr_t)&rh0->ip6r0_len - (caddr_t)ip6);
-		return (-1);
-	}
-
-	if ((addrs = rh0->ip6r0_len / 2) < rh0->ip6r0_segleft) {
-		ip6stat.ip6s_badoptions++;
-		icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER,
-			    (caddr_t)&rh0->ip6r0_segleft - (caddr_t)ip6);
-		return (-1);
-	}
-
-	index = addrs - rh0->ip6r0_segleft;
-	rh0->ip6r0_segleft--;
-	/* note that ip6r0_addr does not exist in RFC2292bis */
-	nextaddr = rh0->ip6r0_addr + index;
-
-	/*
-	 * reject invalid addresses.  be proactive about malicious use of
-	 * IPv4 mapped/compat address.
-	 * XXX need more checks?
-	 */
-	if (IN6_IS_ADDR_MULTICAST(nextaddr) ||
-	    IN6_IS_ADDR_UNSPECIFIED(nextaddr) ||
-	    IN6_IS_ADDR_V4MAPPED(nextaddr) ||
-	    IN6_IS_ADDR_V4COMPAT(nextaddr)) {
-		ip6stat.ip6s_badoptions++;
-		m_freem(m);
-		return (-1);
-	}
-	if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) ||
-	    IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_dst) ||
-	    IN6_IS_ADDR_V4MAPPED(&ip6->ip6_dst) ||
-	    IN6_IS_ADDR_V4COMPAT(&ip6->ip6_dst)) {
-		ip6stat.ip6s_badoptions++;
-		m_freem(m);
-		return (-1);
-	}
-
-	/*
-	 * Swap the IPv6 destination address and nextaddr. Forward the packet.
-	 */
-	tmpaddr = *nextaddr;
-	*nextaddr = ip6->ip6_dst;
-	if (IN6_IS_ADDR_LINKLOCAL(nextaddr))
-		nextaddr->s6_addr16[1] = 0;
-	ip6->ip6_dst = tmpaddr;
-	if (IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst))
-		ip6->ip6_dst.s6_addr16[1] = htons(m->m_pkthdr.rcvif->if_index);
-
-#ifdef COMPAT_RFC1883
-	if (rh0->ip6r0_slmap[index / 8] & (1 << (7 - (index % 8))))
-		ip6_forward(m, IPV6_SRCRT_NEIGHBOR);
-	else
-		ip6_forward(m, IPV6_SRCRT_NOTNEIGHBOR);
-#else
-	ip6_forward(m, 1);
-#endif
-
-	return (-1);			/* m would be freed in ip6_forward() */
-}
-#endif
Index: ./sys/netinet6/in6_pcb.c
===================================================================
RCS file: /home/dcvs/src/sys/netinet6/in6_pcb.c,v
retrieving revision 1.34
diff -u -r1.34 in6_pcb.c
--- ./sys/netinet6/in6_pcb.c	5 Jan 2008 14:02:40 -0000	1.34
+++ ./sys/netinet6/in6_pcb.c	11 Aug 2008 10:09:58 -0000
@@ -879,6 +879,20 @@
 
  		if ((inp->inp_vflag & INP_IPV6) == 0)
 			continue;
+		/*
+		 * If the error designates a new path MTU for a destination
+		 * and the application (associated with this socket) wanted to
+		 * know the value, notify. Note that we notify for all
+		 * disconnected sockets if the corresponding application
+		 * wanted. This is because some UDP applications keep sending
+		 * sockets disconnected.
+		 * XXX: should we avoid to notify the value to TCP sockets?
+		 */
+		if (cmd == PRC_MSGSIZE && (inp->inp_flags & IN6P_MTU) != 0 &&
+		    (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr) ||
+		     IN6_ARE_ADDR_EQUAL(&inp->in6p_faddr, &sa6_dst->sin6_addr))) {
+			ip6_notify_pmtu(inp, (struct sockaddr_in6 *)dst, arg);
+		}
 
 		/*
 		 * Detect if we should notify the error. If no source and
Index: ./sys/netinet6/icmp6.c
===================================================================
RCS file: /home/dcvs/src/sys/netinet6/icmp6.c,v
retrieving revision 1.29
diff -u -r1.29 icmp6.c
--- ./sys/netinet6/icmp6.c	21 Jun 2008 12:30:19 -0000	1.29
+++ ./sys/netinet6/icmp6.c	22 Aug 2008 06:13:20 -0000
@@ -889,7 +889,6 @@
 		int icmp6type = icmp6->icmp6_type;
 		struct ip6_frag *fh;
 		struct ip6_rthdr *rth;
-		struct ip6_rthdr0 *rth0;
 		int rthlen;
 
 		while (1) { /* XXX: should avoid infinite loop explicitly? */
@@ -943,36 +942,6 @@
 				}
 #endif
 				rthlen = (rth->ip6r_len + 1) << 3;
-				/*
-				 * XXX: currently there is no
-				 * officially defined type other
-				 * than type-0.
-				 * Note that if the segment left field
-				 * is 0, all intermediate hops must
-				 * have been passed.
-				 */
-				if (rth->ip6r_segleft &&
-				    rth->ip6r_type == IPV6_RTHDR_TYPE_0) {
-					int hops;
-
-#ifndef PULLDOWN_TEST
-					IP6_EXTHDR_CHECK(m, 0, eoff + rthlen,
-							 -1);
-					rth0 = (struct ip6_rthdr0 *)(mtod(m, caddr_t) + eoff);
-#else
-					IP6_EXTHDR_GET(rth0,
-						       struct ip6_rthdr0 *, m,
-						       eoff, rthlen);
-					if (rth0 == NULL) {
-						icmp6stat.icp6s_tooshort++;
-						return (-1);
-					}
-#endif
-					/* just ignore a bogus header */
-					if ((rth0->ip6r0_len % 2) == 0 &&
-					    (hops = rth0->ip6r0_len/2))
-						finaldst = (struct in6_addr *)(rth0 + 1) + (hops - 1);
-				}
 				eoff += rthlen;
 				nxt = rth->ip6r_nxt;
 				break;
Index: ./lib/libc/net/Makefile.inc
===================================================================
RCS file: /home/dcvs/src/lib/libc/net/Makefile.inc,v
retrieving revision 1.4
diff -u -r1.4 Makefile.inc
--- ./lib/libc/net/Makefile.inc	5 Aug 2005 23:43:19 -0000	1.4
+++ ./lib/libc/net/Makefile.inc	28 Jun 2008 23:51:32 -0000
@@ -33,7 +33,8 @@
 	getifaddrs.3 getipnodebyname.3 \
 	getnameinfo.3 getnetent.3 getprotoent.3 getservent.3 if_indextoname.3 \
 	inet.3 inet_net.3 \
-	inet6_option_space.3 inet6_rthdr_space.3 linkaddr.3 \
+	inet6_opt_init.3 inet6_option_space.3 inet6_rth_space.3 \
+	inet6_rthdr_space.3 linkaddr.3 \
 	rcmd.3 rcmdsh.3 resolver.3
 # not installed: iso_addr.3 ns.3
 
@@ -65,11 +66,22 @@
 	inet.3 inet_ntop.3 inet.3 inet_pton.3 \
 	inet.3 network.3 inet.3 ntoa.3 
 MLINKS+=inet_net.3 inet_net_ntop.3 inet_net.3 inet_net_pton.3
-MLINKS+=inet6_option_space.3 inet6_option_alloc.3 \
+MLINKS+=inet6_opt_init.3 inet6_opt_append.3 \
+	inet6_opt_init.3 inet6_opt_find.3 \
+	inet6_opt_init.3 inet6_opt_finish.3 \
+	inet6_opt_init.3 inet6_opt_get_val.3 \
+	inet6_opt_init.3 inet6_opt_next.3 \
+	inet6_opt_init.3 inet6_opt_set_val.3 \
+	inet6_option_space.3 inet6_option_alloc.3 \
 	inet6_option_space.3 inet6_option_append.3 \
 	inet6_option_space.3 inet6_option_find.3 \
 	inet6_option_space.3 inet6_option_init.3 \
 	inet6_option_space.3 inet6_option_next.3 \
+	inet6_rth_space.3 inet6_rth_add.3 \
+	inet6_rth_space.3 inet6_rth_getaddr.3 \
+	inet6_rth_space.3 inet6_rth_init.3 \
+	inet6_rth_space.3 inet6_rth_reverse.3 \
+	inet6_rth_space.3 inet6_rth_segments.3 \
 	inet6_rthdr_space.3 inet6_rthdr_add.3 \
 	inet6_rthdr_space.3 inet6_rthdr_getaddr.3 \
 	inet6_rthdr_space.3 inet6_rthdr_getflags.3 \
Index: ./lib/libc/net/rthdr.c
===================================================================
RCS file: /home/dcvs/src/lib/libc/net/rthdr.c,v
retrieving revision 1.5
diff -u -r1.5 rthdr.c
--- ./lib/libc/net/rthdr.c	29 May 2007 10:58:11 -0000	1.5
+++ ./lib/libc/net/rthdr.c	24 Aug 2008 02:47:22 -0000
@@ -41,260 +41,61 @@
 
 #include <string.h>
 #include <stdio.h>
+/* 
+ * RFC5095 has deprecated type 0 routing header
+ */
 
 size_t
-inet6_rthdr_space(int type, int seg)
+inet6_rthdr_space(int type __unused, int seg __unused)
 {
-    switch(type) {
-     case IPV6_RTHDR_TYPE_0:
-	 if (seg < 1 || seg > 23)
-	     return(0);
-	 return(CMSG_SPACE(sizeof(struct in6_addr) * (seg - 1)
-			   + sizeof(struct ip6_rthdr0)));
-     default:
-#ifdef DEBUG
-	 fprintf(stderr, "inet6_rthdr_space: unknown type(%d)\n", type);
-#endif 
-	 return(0);
-    }
+	return(0); /* type not suppported */
 }
 
 struct cmsghdr *
-inet6_rthdr_init(void *bp, int type)
+inet6_rthdr_init(void *bp __unused, int type __unused)
 {
-    struct cmsghdr *ch = (struct cmsghdr *)bp;
-    struct ip6_rthdr *rthdr;
-
-    rthdr = (struct ip6_rthdr *)CMSG_DATA(ch);
-
-    ch->cmsg_level = IPPROTO_IPV6;
-    ch->cmsg_type = IPV6_RTHDR;
-
-    switch(type) {
-     case IPV6_RTHDR_TYPE_0:
-	 ch->cmsg_len = CMSG_LEN(sizeof(struct ip6_rthdr0) - sizeof(struct in6_addr));
-	 bzero(rthdr, sizeof(struct ip6_rthdr0));
-	 rthdr->ip6r_type = IPV6_RTHDR_TYPE_0;
-	 return(ch);
-     default:
-#ifdef DEBUG
-	 fprintf(stderr, "inet6_rthdr_init: unknown type(%d)\n", type);
-#endif 
-	 return(NULL);
-    }
+	return(NULL); /* type not suppported */
 }
 
 int
-inet6_rthdr_add(struct cmsghdr *cmsg, const struct in6_addr *addr, u_int flags)
+inet6_rthdr_add(struct cmsghdr *cmsg __unused, const struct in6_addr *addr __unused, u_int flags __unused)
 {
-    struct ip6_rthdr *rthdr;
-
-    rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
-
-    switch(rthdr->ip6r_type) {
-     case IPV6_RTHDR_TYPE_0:
-     {
-	 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
-	 if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT) {
-#ifdef DEBUG
-	     fprintf(stderr, "inet6_rthdr_add: unsupported flag(%d)\n", flags);
-#endif 
-	     return(-1);
-	 }
-	 if (rt0->ip6r0_segleft == 23) {
-#ifdef DEBUG
-	     fprintf(stderr, "inet6_rthdr_add: segment overflow\n");
-#endif 
-	     return(-1);
-	 }
-	 if (flags == IPV6_RTHDR_STRICT) {
-	     int c, b;
-	     c = rt0->ip6r0_segleft / 8;
-	     b = rt0->ip6r0_segleft % 8;
-	     rt0->ip6r0_slmap[c] |= (1 << (7 - b));
-	 }
-	 rt0->ip6r0_segleft++;
-	 bcopy(addr, (caddr_t)rt0 + ((rt0->ip6r0_len + 1) << 3),
-	       sizeof(struct in6_addr));
-	 rt0->ip6r0_len += sizeof(struct in6_addr) >> 3;
-	 cmsg->cmsg_len = CMSG_LEN((rt0->ip6r0_len + 1) << 3);
-	 break;
-     }
-     default:
-#ifdef DEBUG
-	 fprintf(stderr, "inet6_rthdr_add: unknown type(%d)\n",
-		 rthdr->ip6r_type);
-#endif 
-	 return(-1);
-    }
-
-    return(0);
+	return(-1); /* type not suppported */
 }
 
 int
-inet6_rthdr_lasthop(struct cmsghdr *cmsg, unsigned int flags)
+inet6_rthdr_lasthop(struct cmsghdr *cmsg __unused, unsigned int flags __unused)
 {
-    struct ip6_rthdr *rthdr;
-
-    rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
-
-    switch(rthdr->ip6r_type) {
-     case IPV6_RTHDR_TYPE_0:
-     {
-	 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
-	 if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT) {
-#ifdef DEBUG
-	     fprintf(stderr, "inet6_rthdr_lasthop: unsupported flag(%d)\n", flags);
-#endif 
-	     return(-1);
-	 }
-	 if (rt0->ip6r0_segleft > 23) {
-#ifdef DEBUG
-	     fprintf(stderr, "inet6_rthdr_add: segment overflow\n");
-#endif 
-	     return(-1);
-	 }
-	 if (flags == IPV6_RTHDR_STRICT) {
-	     int c, b;
-	     c = rt0->ip6r0_segleft / 8;
-	     b = rt0->ip6r0_segleft % 8;
-	     rt0->ip6r0_slmap[c] |= (1 << (7 - b));
-	 }
-	 break;
-     }
-     default:
-#ifdef DEBUG
-	 fprintf(stderr, "inet6_rthdr_lasthop: unknown type(%d)\n",
-		 rthdr->ip6r_type);
-#endif 
-	 return(-1);
-    }
-
-    return(0);
+	return (-1); /* type not suppported */
 }
 
 #if 0
 int
-inet6_rthdr_reverse(const struct cmsghdr *in, struct cmsghdr *out)
+inet6_rthdr_reverse(const struct cmsghdr *in __unused, struct cmsghdr *out __unused)
 {
 #ifdef DEBUG
     fprintf(stderr, "inet6_rthdr_reverse: not implemented yet\n");
 #endif 
-    return -1;
+    return -1; /* type not suppported */
 }
 #endif
 
 int
-inet6_rthdr_segments(const struct cmsghdr *cmsg)
+inet6_rthdr_segments(const struct cmsghdr *cmsg __unused)
 {
-    struct ip6_rthdr *rthdr;
-
-    rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
-
-    switch(rthdr->ip6r_type) {
-    case IPV6_RTHDR_TYPE_0:
-      {
-	struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
-
-	if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len) {
-#ifdef DEBUG
-	    fprintf(stderr, "inet6_rthdr_segments: invalid size(%d)\n",
-		rt0->ip6r0_len);
-#endif 
-	    return -1;
-	}
-
-	return (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
-      }
-
-    default:
-#ifdef DEBUG
-	fprintf(stderr, "inet6_rthdr_segments: unknown type(%d)\n",
-	    rthdr->ip6r_type);
-#endif 
-	return -1;
-    }
+	return -1; /* type not suppported */
 }
 
 struct in6_addr *
-inet6_rthdr_getaddr(struct cmsghdr *cmsg, int idx)
+inet6_rthdr_getaddr(struct cmsghdr *cmsg __unused, int idx __unused)
 {
-    struct ip6_rthdr *rthdr;
-
-    rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
-
-    switch(rthdr->ip6r_type) {
-    case IPV6_RTHDR_TYPE_0:
-      {
-	struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
-	int naddr;
-
-	if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len) {
-#ifdef DEBUG
-	    fprintf(stderr, "inet6_rthdr_getaddr: invalid size(%d)\n",
-		rt0->ip6r0_len);
-#endif 
-	    return NULL;
-	}
-	naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
-	if (idx <= 0 || naddr < idx) {
-#ifdef DEBUG
-	    fprintf(stderr, "inet6_rthdr_getaddr: invalid idx(%d)\n", idx);
-#endif 
-	    return NULL;
-	}
-	return &rt0->ip6r0_addr[idx - 1];
-      }
-
-    default:
-#ifdef DEBUG
-	fprintf(stderr, "inet6_rthdr_getaddr: unknown type(%d)\n",
-	    rthdr->ip6r_type);
-#endif 
-	return NULL;
-    }
+	return NULL; /* type not suppported */
 }
 
 int
-inet6_rthdr_getflags(const struct cmsghdr *cmsg, int idx)
+inet6_rthdr_getflags(const struct cmsghdr *cmsg __unused, int idx __unused)
 {
-    struct ip6_rthdr *rthdr;
-
-    rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
-
-    switch(rthdr->ip6r_type) {
-    case IPV6_RTHDR_TYPE_0:
-      {
-	struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
-	int naddr;
-
-	if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len) {
-#ifdef DEBUG
-	    fprintf(stderr, "inet6_rthdr_getflags: invalid size(%d)\n",
-		rt0->ip6r0_len);
-#endif 
-	    return -1;
-	}
-	naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
-	if (idx < 0 || naddr < idx) {
-#ifdef DEBUG
-	    fprintf(stderr, "inet6_rthdr_getflags: invalid idx(%d)\n", idx);
-#endif 
-	    return -1;
-	}
-	if (rt0->ip6r0_slmap[idx / 8] & (0x80 >> (idx % 8)))
-	    return IPV6_RTHDR_STRICT;
-	else
-	    return IPV6_RTHDR_LOOSE;
-      }
-
-    default:
-#ifdef DEBUG
-	fprintf(stderr, "inet6_rthdr_getflags: unknown type(%d)\n",
-	    rthdr->ip6r_type);
-#endif 
-	return -1;
-    }
+	return -1; /* type not suppported */
 }
 
 /*
Index: ./crypto/telnet/telnet/commands.c
===================================================================
RCS file: /home/dcvs/src/crypto/telnet/telnet/commands.c,v
retrieving revision 1.2
diff -u -r1.2 commands.c
--- ./crypto/telnet/telnet/commands.c	17 Jun 2003 04:24:37 -0000	1.2
+++ ./crypto/telnet/telnet/commands.c	22 Aug 2008 04:58:02 -0000
@@ -77,7 +77,7 @@
 
 #ifndef       MAXHOSTNAMELEN
 #define       MAXHOSTNAMELEN 256
-#endif        MAXHOSTNAMELEN
+#endif        /* MAXHOSTNAMELEN */
 
 typedef int (*intrtn_t)(int, char **);
 
@@ -2885,7 +2885,7 @@
 
 #ifdef INET6
 	if (ai->ai_family == AF_INET6) {
-		cmsg = inet6_rthdr_init(*cpp, IPV6_RTHDR_TYPE_0);
+		cmsg = NULL;
 		if (*cp != '@')
 			return -1;
 		*protop = IPPROTO_IPV6;
@@ -2961,8 +2961,6 @@
 #ifdef INET6
 		if (res->ai_family == AF_INET6) {
 			sin6 = (struct sockaddr_in6 *)res->ai_addr;
-			inet6_rthdr_add(cmsg, &sin6->sin6_addr,
-					IPV6_RTHDR_LOOSE);
 		} else
 #endif
 	      {
@@ -2980,9 +2978,7 @@
 #ifdef INET6
 		if (res->ai_family == AF_INET6) {
 			if (((char *)CMSG_DATA(cmsg) +
-			     sizeof(struct ip6_rthdr) +
-			     ((inet6_rthdr_segments(cmsg) + 1) *
-			      sizeof(struct in6_addr))) > ep)
+			     sizeof(struct ip6_rthdr)) > ep)
 			return -1;
 		} else
 #endif
@@ -2992,8 +2988,7 @@
 	}
 #ifdef INET6
 	if (res->ai_family == AF_INET6) {
-		inet6_rthdr_lasthop(cmsg, IPV6_RTHDR_LOOSE);
-		*lenp = cmsg->cmsg_len;
+		*lenp = 0;
 	} else
 #endif
       {
Index: ./sys/net/pf/pf_norm.c
===================================================================
RCS file: /home/dcvs/src/sys/net/pf/pf_norm.c,v
retrieving revision 1.9
diff -u -r1.9 pf_norm.c
--- ./sys/net/pf/pf_norm.c	11 Aug 2007 18:53:31 -0000	1.9
+++ ./sys/net/pf/pf_norm.c	25 Jun 2008 16:01:58 -0000
@@ -69,42 +69,6 @@
 /*
  * XXX: This should go to netinet/ip6.h (KAME)
  */
-/* IPv6 options: common part */
-struct ip6_opt {
-	u_int8_t ip6o_type;
-	u_int8_t ip6o_len;
-} __packed;
-
-/* Jumbo Payload Option */
-struct ip6_opt_jumbo {
-	u_int8_t ip6oj_type;
-	u_int8_t ip6oj_len;
-	u_int8_t ip6oj_jumbo_len[4];
-} __packed;
-
-/* NSAP Address Option */
-struct ip6_opt_nsap {
-	u_int8_t ip6on_type;
-	u_int8_t ip6on_len;
-	u_int8_t ip6on_src_nsap_len;
-	u_int8_t ip6on_dst_nsap_len;
-	/* followed by source NSAP */
-	/* followed by destination NSAP */
-} __packed;
-
-/* Tunnel Limit Option */
-struct ip6_opt_tunnel {
-	u_int8_t ip6ot_type;
-	u_int8_t ip6ot_len;
-	u_int8_t ip6ot_encap_limit;
-} __packed;
-
-/* Router Alert Option */
-struct ip6_opt_router {
-	u_int8_t ip6or_type;
-	u_int8_t ip6or_len;
-	u_int8_t ip6or_value[2];
-} __packed;
 #endif /* INET6 */
 
 #define PFFRAG_SEENLAST	0x0001		/* Seen the last fragment for this */
Index: ./sbin/ping6/ping6.c
===================================================================
RCS file: /home/dcvs/src/sbin/ping6/ping6.c,v
retrieving revision 1.8
diff -u -r1.8 ping6.c
--- ./sbin/ping6/ping6.c	5 Mar 2005 22:27:08 -0000	1.8
+++ ./sbin/ping6/ping6.c	22 Aug 2008 07:33:28 -0000
@@ -296,13 +296,16 @@
 	int sockbufsize = 0;
 	int usepktinfo = 0;
 	struct in6_pktinfo *pktinfo = NULL;
-#ifdef USE_RFC2292BIS
+#ifdef USE_RFC3542
 	struct ip6_rthdr *rthdr = NULL;
 #endif
 #ifdef IPSEC_POLICY_IPSEC
 	char *policy_in = NULL;
 	char *policy_out = NULL;
 #endif
+#ifdef IPV6_USE_MIN_MTU
+	int mflag = 0;
+#endif
 	double intval;
 	size_t rthlen;
 
@@ -440,7 +443,7 @@
 			break;
 		case 'm':
 #ifdef IPV6_USE_MIN_MTU
-			options |= F_NOMINMTU;
+			mflag++;
 			break;
 #else
 			errx(1, "-%c is not supported on this platform", ch);
@@ -532,18 +535,10 @@
 		usage();
 		/*NOTREACHED*/
 	}
-
 	if (argc > 1) {
-#ifdef IPV6_RECVRTHDR	/* 2292bis */
-		rthlen = CMSG_SPACE(inet6_rth_space(IPV6_RTHDR_TYPE_0,
-		    argc - 1));
-#else  /* RFC2292 */
-		rthlen = inet6_rthdr_space(IPV6_RTHDR_TYPE_0, argc - 1);
-#endif
-		if (rthlen == 0) {
-			errx(1, "too many intermediate hops");
-			/*NOTREACHED*/
-		}
+		rthlen = 0;
+		errx(1, "too many intermediate hops");
+		/*NOTREACHED*/
 		ip6optlen += rthlen;
 	}
 
@@ -590,7 +585,7 @@
 		int opton = 1;
 
 #ifdef IPV6_RECVHOPOPTS
-		if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVHOPOPTS, &opton,
+	if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVHOPOPTS, &opton,
 		    sizeof(opton)))
 			err(1, "setsockopt(IPV6_RECVHOPOPTS)");
 #else  /* old adv. API */
@@ -669,8 +664,8 @@
 		    &optval, sizeof(optval)) == -1)
 			err(1, "IPV6_MULTICAST_HOPS");
 #ifdef IPV6_USE_MIN_MTU
-	if ((options & F_NOMINMTU) == 0) {
-		optval = 1;
+	if (mflag != 1) {
+		optval = mflag > 1 ? 0 : 1;
 		if (setsockopt(s, IPPROTO_IPV6, IPV6_USE_MIN_MTU,
 		    &optval, sizeof(optval)) == -1)
 			err(1, "setsockopt(IPV6_USE_MIN_MTU)");
@@ -818,60 +813,6 @@
 		scmsgp = CMSG_NXTHDR(&smsghdr, scmsgp);
 	}
 #endif
-
-	if (argc > 1) {	/* some intermediate addrs are specified */
-		int hops, error;
-#ifdef USE_RFC2292BIS
-		int rthdrlen;
-#endif
-
-#ifdef USE_RFC2292BIS
-		rthdrlen = inet6_rth_space(IPV6_RTHDR_TYPE_0, argc - 1);
-		scmsgp->cmsg_len = CMSG_LEN(rthdrlen);
-		scmsgp->cmsg_level = IPPROTO_IPV6;
-		scmsgp->cmsg_type = IPV6_RTHDR;
-		rthdr = (struct ip6_rthdr *)CMSG_DATA(scmsgp);
-		rthdr = inet6_rth_init((void *)rthdr, rthdrlen,
-		    IPV6_RTHDR_TYPE_0, argc - 1);
-		if (rthdr == NULL)
-			errx(1, "can't initialize rthdr");
-#else  /* old advanced API */
-		if ((scmsgp = (struct cmsghdr *)inet6_rthdr_init(scmsgp,
-		    IPV6_RTHDR_TYPE_0)) == 0)
-			errx(1, "can't initialize rthdr");
-#endif /* USE_RFC2292BIS */
-
-		for (hops = 0; hops < argc - 1; hops++) {
-			struct addrinfo *iaip;
-
-			if ((error = getaddrinfo(argv[hops], NULL, &hints,
-			    &iaip)))
-				errx(1, "%s", gai_strerror(error));
-			if (SIN6(iaip->ai_addr)->sin6_family != AF_INET6)
-				errx(1,
-				    "bad addr family of an intermediate addr");
-
-#ifdef USE_RFC2292BIS
-			if (inet6_rth_add(rthdr,
-			    &(SIN6(iaip->ai_addr))->sin6_addr))
-				errx(1, "can't add an intermediate node");
-#else  /* old advanced API */
-			if (inet6_rthdr_add(scmsgp,
-			    &(SIN6(iaip->ai_addr))->sin6_addr,
-			    IPV6_RTHDR_LOOSE))
-				errx(1, "can't add an intermediate node");
-#endif /* USE_RFC2292BIS */
-			freeaddrinfo(iaip);
-		}
-
-#ifndef USE_RFC2292BIS
-		if (inet6_rthdr_lasthop(scmsgp, IPV6_RTHDR_LOOSE))
-			errx(1, "can't set the last flag");
-#endif
-
-		scmsgp = CMSG_NXTHDR(&smsghdr, scmsgp);
-	}
-
 	{
 		/*
 		 * source selection
@@ -886,17 +827,19 @@
 		src.sin6_port = ntohs(DUMMY_PORT);
 		src.sin6_scope_id = dst.sin6_scope_id;
 
-#ifdef USE_RFC2292BIS
+#ifdef USE_RFC3542
 		if (pktinfo &&
 		    setsockopt(dummy, IPPROTO_IPV6, IPV6_PKTINFO,
 		    (void *)pktinfo, sizeof(*pktinfo)))
 			err(1, "UDP setsockopt(IPV6_PKTINFO)");
-
 		if (hoplimit != -1 &&
-		    setsockopt(dummy, IPPROTO_IPV6, IPV6_HOPLIMIT,
+		    setsockopt(dummy, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
 		    (void *)&hoplimit, sizeof(hoplimit)))
-			err(1, "UDP setsockopt(IPV6_HOPLIMIT)");
-
+			err(1, "UDP setsockopt(IPV6_UNICAST_HOPS)");
+		if (hoplimit != -1 &&
+		    setsockopt(dummy, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
+		    (void *)&hoplimit, sizeof(hoplimit)))
+			err(1, "UDP setsockopt(IPV6_MULTICAST_HOPS)");
 		if (rthdr &&
 		    setsockopt(dummy, IPPROTO_IPV6, IPV6_RTHDR,
 		    (void *)rthdr, (rthdr->ip6r_len + 1) << 3))
@@ -1664,7 +1607,7 @@
 	}
 }
 
-#ifdef USE_RFC2292BIS
+#ifdef USE_RFC3542
 void
 pr_ip6opt(void *extbuf)
 {
@@ -1715,7 +1658,7 @@
 	}
 	return;
 }
-#else  /* !USE_RFC2292BIS */
+#else  /* !USE_RFC3542 */
 /* ARGSUSED */
 void
 pr_ip6opt(void *extbuf __unused)
@@ -1723,9 +1666,9 @@
 	putchar('\n');
 	return;
 }
-#endif /* USE_RFC2292BIS */
+#endif /* USE_RFC3542 */
 
-#ifdef USE_RFC2292BIS
+#ifdef USE_RFC3542
 void
 pr_rthdr(void *extbuf)
 {
@@ -1737,29 +1680,13 @@
 	/* print fixed part of the header */
 	printf("nxt %u, len %u (%d bytes), type %u, ", rh->ip6r_nxt,
 	    rh->ip6r_len, (rh->ip6r_len + 1) << 3, rh->ip6r_type);
-	if ((segments = inet6_rth_segments(extbuf)) >= 0)
-		printf("%d segments, ", segments);
-	else
-		printf("segments unknown, ");
+	printf("segments unknown, ");
 	printf("%d left\n", rh->ip6r_segleft);
-
-	for (i = 0; i < segments; i++) {
-		in6 = inet6_rth_getaddr(extbuf, i);
-		if (in6 == NULL)
-			printf("   [%d]<NULL>\n", i);
-		else {
-			if (!inet_ntop(AF_INET6, in6, ntopbuf,
-			    sizeof(ntopbuf)))
-				strncpy(ntopbuf, "?", sizeof(ntopbuf));
-			printf("   [%d]%s\n", i, ntopbuf);
-		}
-	}
-
 	return;
 
 }
 
-#else  /* !USE_RFC2292BIS */
+#else  /* !USE_RFC3542 */
 /* ARGSUSED */
 void
 pr_rthdr(void *extbuf __unused)
@@ -1767,7 +1694,7 @@
 	putchar('\n');
 	return;
 }
-#endif /* USE_RFC2292BIS */
+#endif /* USE_RFC3542 */
 
 int
 pr_bitrange(u_int32_t v, int ss, int ii)
Index: ./sbin/ping6/Makefile
===================================================================
RCS file: /home/dcvs/src/sbin/ping6/Makefile,v
retrieving revision 1.4
diff -u -r1.4 Makefile
--- ./sbin/ping6/Makefile	17 Oct 2006 00:55:43 -0000	1.4
+++ ./sbin/ping6/Makefile	13 Aug 2008 09:55:29 -0000
@@ -4,7 +4,7 @@
 PROG=	ping6
 MAN=	ping6.8
 
-CFLAGS+=-DINET6 -DIPSEC
+CFLAGS+=-DINET6 -DIPSEC -DUSE_RFC3542
 
 BINOWN=	root
 BINMODE=4555
Index: ./lib/libcompat/4.3/rexec.c
===================================================================
RCS file: /home/dcvs/src/lib/libcompat/4.3/rexec.c,v
retrieving revision 1.3
diff -u -r1.3 rexec.c
--- ./lib/libcompat/4.3/rexec.c	30 Jul 2006 07:50:28 -0000	1.3
+++ ./lib/libcompat/4.3/rexec.c	25 Jun 2008 12:56:52 -0000
@@ -295,6 +295,147 @@
 }
 
 int
+rexec_af(ahost, rport, name, pass, cmd, fd2p, af)
+	char **ahost;
+	int rport;
+	const char *name, *pass, *cmd;
+	int *fd2p;
+	sa_family_t af;
+{
+	struct sockaddr_storage sa2, from;
+	struct addrinfo hints, *res0;
+	const char *orig_name = name;
+	const char *orig_pass = pass;
+	static char *ahostbuf;
+	u_short port = 0;
+	int s, timo = 1, s3;
+	char c;
+	int gai;
+	char servbuff[NI_MAXSERV];
+
+	sprintf(servbuff, sizeof(servbuff), "%d", ntohs(rport));
+	servbuff[sizeof(servbuff) - 1] = '\0';
+
+	memset(&hints, '\0', sizeof(hints));
+	hints.ai_family = af;
+	hints.ai_socktype = SOCK_STREAM;
+	hints.ai_flags = AI_CANONNAME;
+	gai = getaddrinfo(*ahost, servbuff, &hints, &res0);
+	if (gai){
+		/* XXX: set errno? */
+		return -1;
+	}
+
+	if (res0->ai_canonname){
+		free (ahostbuf);
+		ahostbuf = strdup (res0->ai_canonname);
+		if (ahostbuf == NULL) {
+			perror ("rexec: strdup");
+			return (-1);
+		}
+		*ahost = ahostbuf;
+	} else {
+		*ahost = NULL;
+		__set_errno (ENOENT);
+		return -1;
+	}
+	ruserpass(res0->ai_canonname, &name, &pass);
+retry:
+	s = socket(res0->ai_family, res0->ai_socktype, 0);
+	if (s < 0) {
+		perror("rexec: socket");
+		return (-1);
+	}
+	if (connect(s, res0->ai_addr, res0->ai_addrlen) < 0) {
+		if (errno == ECONNREFUSED && timo <= 16) {
+			(void) close(s);
+			sleep(timo);
+			timo *= 2;
+			goto retry;
+		}
+		perror(res0->ai_canonname);
+		return (-1);
+	}
+	if (fd2p == 0) {
+		(void) write(s, "", 1);
+		port = 0;
+	} else {
+		char num[32];
+		int s2;
+		socklen_t sa2len;
+
+		s2 = socket(res0->ai_family, res0->ai_socktype, 0);
+		if (s2 < 0) {
+			(void) close(s);
+			return (-1);
+		}
+		listen(s2, 1);
+		sa2len = sizeof (sa2);
+		if (getsockname(s2, (struct sockaddr *)&sa2, &sa2len) < 0) {
+			perror("getsockname");
+			(void) close(s2);
+			goto bad;
+		} else if (sa2len != SA_LEN((struct sockaddr *)&sa2)) {
+			__set_errno(EINVAL);
+			(void) close(s2);
+			goto bad;
+		}
+		port = 0;
+		if (!getnameinfo((struct sockaddr *)&sa2, sa2len,
+				 NULL, 0, servbuff, sizeof(servbuff),
+				 NI_NUMERICSERV))
+			port = atoi(servbuff);
+		(void) sprintf(num, "%u", port);
+		(void) write(s, num, strlen(num)+1);
+		{ socklen_t len = sizeof (from);
+		  s3 = accept(s2, (struct sockaddr *)&from,
+						  &len);
+		  close(s2);
+		  if (s3 < 0) {
+			perror("accept");
+			port = 0;
+			goto bad;
+		  }
+		}
+		*fd2p = s3;
+	}
+
+	(void) write(s, name, strlen(name) + 1);
+	/* should public key encypt the password here */
+	(void) write(s, pass, strlen(pass) + 1);
+	(void) write(s, cmd, strlen(cmd) + 1);
+
+	/* We don't need the memory allocated for the name and the password
+	   in ruserpass anymore.  */
+	if (name != orig_name)
+	  free ((char *) name);
+	if (pass != orig_pass)
+	  free ((char *) pass);
+
+	if (read(s, &c, 1) != 1) {
+		perror(*ahost);
+		goto bad;
+	}
+	if (c != 0) {
+		while (read(s, &c, 1) == 1) {
+			(void) write(2, &c, 1);
+			if (c == '\n')
+				break;
+		}
+		goto bad;
+	}
+	freeaddrinfo(res0);
+	return (s);
+bad:
+	if (port)
+		(void) close(*fd2p);
+	(void) close(s);
+	freeaddrinfo(res0);
+	return (-1);
+}
+
+
+int
 rexec(ahost, rport, name, pass, cmd, fd2p)
 	char **ahost;
 	int rport;
Index: ./usr.bin/telnet/commands.c
===================================================================
RCS file: /home/dcvs/src/usr.bin/telnet/commands.c,v
retrieving revision 1.6
diff -u -r1.6 commands.c
--- ./usr.bin/telnet/commands.c	18 May 2007 17:05:12 -0000	1.6
+++ ./usr.bin/telnet/commands.c	22 Aug 2008 07:37:18 -0000
@@ -2568,6 +2568,7 @@
 #ifdef INET6
 	struct sockaddr_in6 *sin6;
 	struct cmsghdr *cmsg;
+	struct ip6_rthdr *rth;
 #endif
 	struct addrinfo hints, *res;
 	int error;
@@ -2610,11 +2611,18 @@
 
 #ifdef INET6
 	if (ai->ai_family == AF_INET6) {
-		cmsg = inet6_rthdr_init(*cpp, IPV6_RTHDR_TYPE_0);
+	/* 
+	 *RFC3542 has obsoleted IPV6_PKTOPTIONS socket option. 
+	 */
+#ifdef COMPAT_RFC1883		/* XXX */
+		cmsg = NULL;
 		if (*cp != '@')
 			return -1;
 		*protop = IPPROTO_IPV6;
 		*optp = IPV6_PKTOPTIONS;
+#else
+		return -1;
+#endif /* COMPAT_RFC1883 */
 	} else
 #endif
       {
@@ -2685,9 +2693,7 @@
 		}
 #ifdef INET6
 		if (res->ai_family == AF_INET6) {
-			sin6 = (struct sockaddr_in6 *)res->ai_addr;
-			inet6_rthdr_add(cmsg, &sin6->sin6_addr,
-					IPV6_RTHDR_LOOSE);
+			return(0);
 		} else
 #endif
 	      {
@@ -2703,13 +2709,13 @@
 		 * Check to make sure there is space for next address
 		 */
 #ifdef INET6
+#ifdef COMPAT_RFC1883		/* XXX */
 		if (res->ai_family == AF_INET6) {
 			if (((char *)CMSG_DATA(cmsg) +
-			     sizeof(struct ip6_rthdr) +
-			     ((inet6_rthdr_segments(cmsg) + 1) *
-			      sizeof(struct in6_addr))) > ep)
+			     sizeof(struct ip6_rthdr)) > ep)
 			return -1;
 		} else
+#endif /* COMPAT_RFC1883 */
 #endif
 		if (lsrp + 4 > ep)
 			return -1;
@@ -2717,8 +2723,9 @@
 	}
 #ifdef INET6
 	if (res->ai_family == AF_INET6) {
-		inet6_rthdr_lasthop(cmsg, IPV6_RTHDR_LOOSE);
-		*lenp = cmsg->cmsg_len;
+#ifdef COMPAT_RFC1883		/* XXX */
+		*lenp = 0;
+#endif /* COMPAT_RFC1883 */
 	} else
 #endif
       {
Index: ./usr.sbin/traceroute6/traceroute6.c
===================================================================
RCS file: /home/dcvs/src/usr.sbin/traceroute6/traceroute6.c,v
retrieving revision 1.8
diff -u -r1.8 traceroute6.c
--- ./usr.sbin/traceroute6/traceroute6.c	20 May 2008 12:14:10 -0000	1.8
+++ ./usr.sbin/traceroute6/traceroute6.c	22 Aug 2008 09:28:44 -0000
@@ -331,10 +331,6 @@
 #define	ICMP6ECHOLEN	8
 /* XXX: 2064 = 127(max hops in type 0 rthdr) * sizeof(ip6_hdr) + 16(margin) */
 char rtbuf[2064];
-#ifdef USE_RFC2292BIS
-struct ip6_rthdr *rth;
-#endif
-struct cmsghdr *cmsg;
 
 char *source = 0;
 char *hostname;
@@ -423,33 +419,6 @@
 				    "traceroute6: unknown host %s\n", optarg);
 				exit(1);
 			}
-#ifdef USE_RFC2292BIS
-			if (rth == NULL) {
-				/*
-				 * XXX: We can't detect the number of
-				 * intermediate nodes yet.
-				 */
-				if ((rth = inet6_rth_init((void *)rtbuf,
-				    sizeof(rtbuf), IPV6_RTHDR_TYPE_0,
-				    0)) == NULL) {
-					fprintf(stderr,
-					    "inet6_rth_init failed.\n");
-					exit(1);
-				}
-			}
-			if (inet6_rth_add((void *)rth,
-			    (struct in6_addr *)hp->h_addr)) {
-				fprintf(stderr,
-				    "inet6_rth_add failed for %s\n",
-				    optarg);
-				exit(1);
-			}
-#else  /* old advanced API */
-			if (cmsg == NULL)
-				cmsg = inet6_rthdr_init(rtbuf, IPV6_RTHDR_TYPE_0);
-			inet6_rthdr_add(cmsg, (struct in6_addr *)hp->h_addr,
-			    IPV6_RTHDR_LOOSE);
-#endif
 			freehostent(hp);
 			break;
 		case 'I':
@@ -729,27 +698,6 @@
 	if (options & SO_DONTROUTE)
 		(void) setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE,
 		    (char *)&on, sizeof(on));
-#ifdef USE_RFC2292BIS
-	if (rth) {/* XXX: there is no library to finalize the header... */
-		rth->ip6r_len = rth->ip6r_segleft * 2;
-		if (setsockopt(sndsock, IPPROTO_IPV6, IPV6_RTHDR,
-		    (void *)rth, (rth->ip6r_len + 1) << 3)) {
-			fprintf(stderr, "setsockopt(IPV6_RTHDR): %s\n",
-			    strerror(errno));
-			exit(1);
-		}
-	}
-#else  /* old advanced API */
-	if (cmsg != NULL) {
-		inet6_rthdr_lasthop(cmsg, IPV6_RTHDR_LOOSE);
-		if (setsockopt(sndsock, IPPROTO_IPV6, IPV6_PKTOPTIONS,
-		    rtbuf, cmsg->cmsg_len) < 0) {
-			fprintf(stderr, "setsockopt(IPV6_PKTOPTIONS): %s\n",
-			    strerror(errno));
-			exit(1);
-		}
-	}
-#endif /* USE_RFC2292BIS */
 #ifdef IPSEC
 #ifdef IPSEC_POLICY_IPSEC
 	/*
@@ -815,9 +763,6 @@
 
 		Nxt = Dst;
 		Nxt.sin6_port = htons(DUMMY_PORT);
-		if (cmsg != NULL)
-			bcopy(inet6_rthdr_getaddr(cmsg, 1), &Nxt.sin6_addr,
-			    sizeof(Nxt.sin6_addr));
 		if ((dummy = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
 			perror("socket");
 			exit(1);
Index: ./usr.sbin/traceroute6/Makefile
===================================================================
RCS file: /home/dcvs/src/usr.sbin/traceroute6/Makefile,v
retrieving revision 1.2
diff -u -r1.2 Makefile
--- ./usr.sbin/traceroute6/Makefile	17 Jun 2003 04:30:03 -0000	1.2
+++ ./usr.sbin/traceroute6/Makefile	13 Aug 2008 09:58:18 -0000
@@ -19,7 +19,7 @@
 BINOWN=	root
 BINMODE=4555
 
-CFLAGS+=-DINET6 -DIPSEC -DHAVE_POLL
+CFLAGS+=-DINET6 -DIPSEC -DHAVE_POLL -DUSE_RFC3542
 
 DPADD=	${LIBIPSEC}
 LDADD=	-lipsec
--- /dev/null	2008-08-23 03:07:49 +0000
+++ ./lib/libc/net/inet6_rth_space.3	2008-08-14 11:15:32 +0000
@@ -0,0 +1,225 @@
+.\"	$KAME: inet6_rth_space.3,v 1.7 2005/01/05 03:00:44 itojun Exp $
+.\"
+.\" Copyright (C) 2004 WIDE Project.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the project nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD: src/lib/libc/net/inet6_rth_space.3,v 1.10 2005/07/31 03:30:44 keramida Exp $
+.\" $DragonFly$
+.\"
+.Dd December 24, 2004
+.Dt INET6_RTH_SPACE 3
+.Os
+.\"
+.Sh NAME
+.Nm inet6_rth_space ,
+.Nm inet6_rth_init ,
+.Nm inet6_rth_add ,
+.Nm inet6_rth_reverse ,
+.Nm inet6_rth_segments ,
+.Nm inet6_rth_getaddr
+.Nd IPv6 Routing Header Options manipulation
+.\"
+.Sh SYNOPSIS
+.In netinet/in.h
+.Ft socklen_t
+.Fn inet6_rth_space "int" "int"
+.Ft "void *"
+.Fn inet6_rth_init "void *" "socklen_t" "int" "int"
+.Ft int
+.Fn inet6_rth_add "void *" "const struct in6_addr *"
+.Ft int
+.Fn inet6_rth_reverse "const void *" "void *"
+.Ft int
+.Fn inet6_rth_segments "const void *"
+.Ft "struct in6_addr *"
+.Fn inet6_rth_getaddr "const void *" "int"
+.\"
+.Sh DESCRIPTION
+The IPv6 Advanced API, RFC 3542, defines the functions that an
+application calls to build and examine IPv6 Routing headers.
+Routing headers are used to perform source routing in IPv6 networks.
+The RFC uses the word
+.Dq segments
+to describe addresses and that is the term used here as well.
+All of the functions are defined in the
+.In netinet/in.h
+header file.
+The functions described in this manual page all operate
+on routing header structures which are defined in
+.In netinet/ip6.h
+but which should not need to be modified outside the use of this API.
+The size and shape of the route header structures may change, so using
+the APIs is a more portable, long term, solution.
+.Pp
+The functions in the API are split into two groups, those that build a
+routing header and those that parse a received routing header.
+We will describe the builder functions followed by the parser functions.
+.Ss inet6_rth_space
+The
+.Fn inet6_rth_space
+function returns the number of bytes required to hold a Routing Header
+of the type, specified in the
+.Fa type
+argument and containing the number of addresses specified in the
+.Fa segments
+argument.
+When the type is
+.Dv IPV6_RTHDR_TYPE_0
+the number of segments must be from 0 through 127.
+Routing headers of type
+.Dv IPV6_RTHDR_TYPE_2
+contain only one segment, and are only used with Mobile IPv6.
+The return value from this function is the number of bytes required to
+store the routing header.
+If the value 0 is returned then either the
+route header type was not recognized or another error occurred.
+.Ss inet6_rth_init
+The
+.Fn inet6_rth_init
+function initializes the pre-allocated buffer pointed to by
+.Fa bp
+to contain a routing header of the specified type The
+.Fa bp_len
+argument is used to verify that the buffer is large enough.
+The caller must allocate the buffer pointed to by bp.
+The necessary buffer size should be determined by calling
+.Fn inet6_rth_space
+described in the previous sections.
+.Pp
+The
+.Fn inet6_rth_init
+function returns a pointer to
+.Fa bp
+on success and
+.Dv NULL
+when there is an error.
+.Ss inet6_rth_add
+The
+.Fn inet6_rth_add
+function adds the IPv6 address pointed to by
+.Fa addr
+to the end of the routing header being constructed.
+.Pp
+A successful addition results in the function returning 0, otherwise
+\-1 is returned.
+.Ss inet6_rth_reverse
+The
+.Fn inet6_rth_reverse
+function takes a routing header, pointed to by the
+argument
+.Fa in ,
+and writes a new routing header into the argument pointed to by
+.Fa out .
+The routing header at that sends datagrams along the reverse of that
+route.
+Both arguments are allowed to point to the same buffer meaning
+that the reversal can occur in place.
+.Pp
+The return value of the function is 0 on success, or \-1 when
+there is an error.
+.\"
+.Pp
+The next set of functions operate on a routing header that the
+application wants to parse.
+In the usual case such a routing header
+is received from the network, although these functions can also be
+used with routing headers that the application itself created.
+.Ss inet6_rth_segments
+The
+.Fn inet6_rth_segments
+function returns the number of segments contained in the
+routing header pointed to by
+.Fa bp .
+The return value is the number of segments contained in the routing
+header, or \-1 if an error occurred.
+It is not an error for 0 to be
+returned as a routing header may contain 0 segments.
+.\"
+.Ss inet6_rth_getaddr
+The
+.Fn inet6_rth_getaddr
+function is used to retrieve a single address from a routing header.
+The
+.Fa index
+is the location in the routing header from which the application wants
+to retrieve an address.
+The
+.Fa index
+parameter must have a value between 0 and one less than the number of
+segments present in the routing header.
+The
+.Fn inet6_rth_segments
+function, described in the last section, should be used to determine
+the total number of segments in the routing header.
+The
+.Fn inet6_rth_getaddr
+function returns a pointer to an IPv6 address on success or
+.Dv NULL
+when an error has occurred.
+.\"
+.Sh EXAMPLES
+RFC 3542 gives extensive examples in Section 21, Appendix B.
+.Pp
+KAME also provides examples in the advapitest directory of its kit.
+.\"
+.Sh DIAGNOSTICS
+The
+.Fn inet6_rth_space
+and
+.Fn inet6_rth_getaddr
+functions return 0 on errors.
+.Pp
+The
+.Fn inet6_rthdr_init
+function returns
+.Dv NULL
+on error.
+The
+.Fn inet6_rth_add
+and
+.Fn inet6_rth_reverse
+functions return 0 on success, or \-1 upon an error.
+.\"
+.Sh SEE ALSO
+.Rs
+.%A W. Stevens
+.%A M. Thomas
+.%A E. Nordmark
+.%A T. Jinmei
+.%T "Advanced Sockets API for IPv6"
+.%N RFC 3542
+.%D May 2003
+.Re
+.Rs
+.%A S. Deering
+.%A R. Hinden
+.%T "Internet Protocol, Version 6 (IPv6) Specification"
+.%N RFC2460
+.%D December 1998
+.Re
+.Sh HISTORY
+The implementation first appeared in KAME advanced networking kit.
--- /dev/null	2008-08-23 03:07:49 +0000
+++ ./lib/libc/net/inet6_opt_init.3	2008-08-14 11:15:22 +0000
@@ -0,0 +1,338 @@
+.\"	$KAME: inet6_opt_init.3,v 1.7 2004/12/27 05:08:23 itojun Exp $
+.\"
+.\" Copyright (C) 2004 WIDE Project.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the project nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD: src/lib/libc/net/inet6_opt_init.3,v 1.10 2005/11/23 16:07:54 ru Exp $
+.\" $DragonFly$
+.\"
+.Dd December 23, 2004
+.Dt INET6_OPT_INIT 3
+.Os
+.\"
+.Sh NAME
+.Nm inet6_opt_init ,
+.Nm inet6_opt_append ,
+.Nm inet6_opt_finish ,
+.Nm inet6_opt_set_val ,
+.Nm inet6_opt_next ,
+.Nm inet6_opt_find ,
+.Nm inet6_opt_get_val
+.Nd IPv6 Hop-by-Hop and Destination Options manipulation
+.\"
+.Sh SYNOPSIS
+.In netinet/in.h
+.Ft "int"
+.Fn inet6_opt_init "void *extbuf" "socklen_t extlen"
+.Ft "int"
+.Fn inet6_opt_append "void *extbuf" "socklen_t extlen" "int offset" "u_int8_t type" "socklen_t len" "u_int8_t align" "void **databufp"
+.Ft "int"
+.Fn inet6_opt_finish "void *extbuf" "socklen_t extlen" "int offset"
+.Ft "int"
+.Fn inet6_opt_set_val "void *databuf" "int offset" "void *val" "socklen_t vallen"
+.Ft "int"
+.Fn inet6_opt_next "void *extbuf" "socklen_t extlen" "int offset" "u_int8_t *typep" "socklen_t *lenp" "void **databufp"
+.Ft "int"
+.Fn inet6_opt_find "void *extbuf" "socklen_t extlen" "int offset" "u_int8_t type" "socklen_t *lenp" "void **databufp"
+.Ft "int"
+.Fn inet6_opt_get_val "void *databuf" "int offset" "void *val" "socklen_t vallen"
+.\"
+.Sh DESCRIPTION
+Building and parsing the Hop-by-Hop and Destination options is
+complicated.
+The advanced sockets API defines a set of functions to
+help applications create and manipulate Hop-by-Hop and Destination
+options.
+This man page describes the functions specified in
+IETF Draft RFC3542.
+These functions use the
+formatting rules specified in Appendix B in RFC2460, i.e., that the
+largest field is placed last in the option.
+The function prototypes
+for these functions are all contained in the
+.In netinet/in.h
+header file.
+.\"
+.Ss inet6_opt_init
+The
+.Fn inet6_opt_init
+function
+returns the number of bytes needed for an empty
+extension header, one without any options.
+If the
+.Fa extbuf
+argument points to a valid section of memory
+then the
+.Fn inet6_opt_init
+function also initializes the extension header's length field.
+When attempting to initialize an extension buffer passed in the
+.Fa extbuf
+argument,
+.Fa extlen
+must be a positive multiple of 8 or else the function fails and
+returns \-1 to the caller.
+.\"
+.Ss inet6_opt_append
+The
+.Fn inet6_opt_append
+function can perform two different jobs.
+When a valid
+.Fa extbuf
+argument is supplied it appends an option to the extension buffer and
+returns the updated total length as well as a pointer to the newly
+created option in
+.Fa databufp .
+If the value
+of
+.Fa extbuf
+is
+.Dv NULL
+then the
+.Fn inet6_opt_append
+function only reports what the total length would
+be if the option were actually appended.
+The
+.Fa len
+and
+.Fa align
+arguments specify the length of the option and the required data
+alignment which must be used when appending the option.
+The
+.Fa offset
+argument should be the length returned by the
+.Fn inet6_opt_init
+function or a previous call to
+.Fn inet6_opt_append .
+.Pp
+The
+.Fa type
+argument is the 8-bit option type.
+.Pp
+After
+.Fn inet6_opt_append
+has been called, the application can use the buffer pointed to by
+.Fa databufp
+directly, or use
+.Fn inet6_opt_set_val
+to specify the data to be contained in the option.
+.Pp
+Option types of
+.Li 0
+and
+.Li 1
+are reserved for the
+.Li Pad1
+and
+.Li PadN
+options.
+All other values from 2 through 255 may be used by applications.
+.Pp
+The length of the option data is contained in an 8-bit value and so
+may contain any value from 0 through 255.
+.Pp
+The
+.Fa align
+parameter must have a value of 1, 2, 4, or 8 and cannot exceed the
+value of
+.Fa len .
+The alignment values represent no alignment, 16 bit, 32 bit and 64 bit
+alignments, respectively.
+.\"
+.Ss inet6_opt_finish
+The
+.Fn inet6_opt_finish
+function
+calculates the final padding necessary to make the extension header a
+multiple of 8 bytes, as required by the IPv6 extension header
+specification, and returns the extension header's updated total
+length.
+The
+.Fa offset
+argument should be the length returned by
+.Fn inet6_opt_init
+or
+.Fn inet6_opt_append .
+When
+.Fa extbuf
+is not
+.Dv NULL
+the function also sets up the appropriate padding bytes by inserting a
+Pad1 or PadN option of the proper length.
+.Pp
+If the extension header is too small to contain the proper padding
+then an error of \-1 is returned to the caller.
+.\"
+.Ss inet6_opt_set_val
+The
+.Fn inet6_opt_set_val
+function inserts data items of various sizes into the data portion of
+the option.
+The
+.Fa databuf
+argument is a pointer to memory that was returned by the
+.Fn inet6_opt_append
+call and the
+.Fa offset
+argument specifies where the option should be placed in the
+data buffer.
+The
+.Fa val
+argument points to an area of memory containing the data to be
+inserted into the extension header, and the
+.Fa vallen
+argument indicates how much data to copy.
+.Pp
+The caller should ensure that each field is aligned on its natural
+boundaries as described in Appendix B of RFC2460.
+.Pp
+The function returns the offset for the next field which is calculated as
+.Fa offset
++
+.Fa vallen
+and is used when composing options with multiple fields.
+.\"
+.Ss inet6_opt_next
+The
+.Fn inet6_opt_next
+function parses received extension headers.
+The
+.Fa extbuf
+and
+.Fa extlen
+arguments specify the location and length of the extension header
+being parsed.
+The
+.Fa offset
+argument should either be zero, for the first option, or the length value
+returned by a previous call to
+.Fn inet6_opt_next
+or
+.Fn inet6_opt_find .
+The return value specifies the position where to continue scanning the
+extension buffer.
+The option is returned in the arguments
+.Fa typep , lenp ,
+and
+.Fa databufp ,
+which
+point to the 8-bit option type, the 8-bit option length and the option
+data, respectively.
+This function does not return any PAD1 or PADN options.
+When an error occurs or there are no more options, the return
+value is \-1.
+.\"
+.Ss inet6_opt_find
+The
+.Fn inet6_opt_find
+function searches the extension buffer for a particular option type,
+passed in through the
+.Fa type
+argument.
+If the option is found then the
+.Fa lenp
+and
+.Fa databufp
+arguments are updated to point to the option's length and data,
+respectively.
+The
+.Fa extbuf
+and
+.Fa extlen
+arguments
+must point to a valid extension buffer and give its length.
+The
+.Fa offset
+argument can be used to search from a location anywhere in the
+extension header.
+.Ss inet6_opt_get_val
+The
+.Fn inet6_opt_get_val
+function extracts data items of various sizes in the data portion of
+the option.
+The
+.Fa databuf
+is a pointer returned by the
+.Fn inet6_opt_next
+or
+.Fn inet6_opt_find
+functions.
+The
+.Fa val
+argument points where the data will be extracted.
+The
+.Fa offset
+argument specifies from where in the data portion of the option the
+value should be extracted; the first byte of option data is specified
+by an offset of zero.
+.Pp
+It is expected that each field is aligned on its natural boundaries as
+described in Appendix B of RFC2460.
+.Pp
+The function returns the offset for the next field
+by calculating
+.Fa offset
++
+.Fa vallen
+which can be used when extracting option content with multiple fields.
+Robust receivers must verify alignment before calling this function.
+.\"
+.Sh RETURN VALUES
+All the functions return
+\-1
+on an error.
+.\"
+.Sh EXAMPLES
+RFC3542 gives comprehensive examples in Section 23.
+.Pp
+KAME also provides examples in the
+.Pa advapitest
+directory of its kit.
+.\"
+.Sh SEE ALSO
+.Rs
+.%A W. Stevens
+.%A M. Thomas
+.%A E. Nordmark
+.%A T. Jinmei
+.%T "Advanced Sockets API for IPv6"
+.%N RFC3542
+.%D October 2002
+.Re
+.Rs
+.%A S. Deering
+.%A R. Hinden
+.%T "Internet Protocol, Version 6 (IPv6) Specification"
+.%N RFC2460
+.%D December 1998
+.Re
+.Sh STANDARDS
+The functions are documented in
+.Dq Advanced Sockets API for IPv6
+.Pq RFC3542 .
+.\"
+.Sh HISTORY
+The implementation first appeared in KAME advanced networking kit.
