xref: /onnv-gate/usr/src/uts/common/inet/ip/icmp.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate /* Copyright (c) 1990 Mentat Inc. */
27*0Sstevel@tonic-gate 
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
30*0Sstevel@tonic-gate 
31*0Sstevel@tonic-gate #include <sys/types.h>
32*0Sstevel@tonic-gate #include <sys/stream.h>
33*0Sstevel@tonic-gate #include <sys/stropts.h>
34*0Sstevel@tonic-gate #include <sys/strlog.h>
35*0Sstevel@tonic-gate #include <sys/strsun.h>
36*0Sstevel@tonic-gate #define	_SUN_TPI_VERSION 2
37*0Sstevel@tonic-gate #include <sys/tihdr.h>
38*0Sstevel@tonic-gate #include <sys/timod.h>
39*0Sstevel@tonic-gate #include <sys/ddi.h>
40*0Sstevel@tonic-gate #include <sys/sunddi.h>
41*0Sstevel@tonic-gate #include <sys/cmn_err.h>
42*0Sstevel@tonic-gate #include <sys/debug.h>
43*0Sstevel@tonic-gate #include <sys/kmem.h>
44*0Sstevel@tonic-gate #include <sys/policy.h>
45*0Sstevel@tonic-gate #include <sys/zone.h>
46*0Sstevel@tonic-gate 
47*0Sstevel@tonic-gate #include <sys/socket.h>
48*0Sstevel@tonic-gate #include <sys/isa_defs.h>
49*0Sstevel@tonic-gate #include <sys/suntpi.h>
50*0Sstevel@tonic-gate #include <sys/xti_inet.h>
51*0Sstevel@tonic-gate 
52*0Sstevel@tonic-gate #include <net/route.h>
53*0Sstevel@tonic-gate #include <net/if.h>
54*0Sstevel@tonic-gate 
55*0Sstevel@tonic-gate #include <netinet/in.h>
56*0Sstevel@tonic-gate #include <netinet/ip6.h>
57*0Sstevel@tonic-gate #include <netinet/icmp6.h>
58*0Sstevel@tonic-gate #include <inet/common.h>
59*0Sstevel@tonic-gate #include <inet/ip.h>
60*0Sstevel@tonic-gate #include <inet/ip6.h>
61*0Sstevel@tonic-gate #include <inet/ip_ire.h>
62*0Sstevel@tonic-gate #include <inet/mi.h>
63*0Sstevel@tonic-gate #include <inet/nd.h>
64*0Sstevel@tonic-gate #include <inet/optcom.h>
65*0Sstevel@tonic-gate #include <inet/snmpcom.h>
66*0Sstevel@tonic-gate #include <inet/kstatcom.h>
67*0Sstevel@tonic-gate #include <inet/rawip_impl.h>
68*0Sstevel@tonic-gate 
69*0Sstevel@tonic-gate #include <netinet/ip_mroute.h>
70*0Sstevel@tonic-gate #include <inet/tcp.h>
71*0Sstevel@tonic-gate #include <net/pfkeyv2.h>
72*0Sstevel@tonic-gate #include <inet/ipsec_info.h>
73*0Sstevel@tonic-gate #include <inet/ipclassifier.h>
74*0Sstevel@tonic-gate 
75*0Sstevel@tonic-gate #define	ICMP6 "icmp6"
76*0Sstevel@tonic-gate major_t	ICMP6_MAJ;
77*0Sstevel@tonic-gate 
78*0Sstevel@tonic-gate /*
79*0Sstevel@tonic-gate  * Object to represent database of options to search passed to
80*0Sstevel@tonic-gate  * {sock,tpi}optcom_req() interface routine to take care of option
81*0Sstevel@tonic-gate  * management and associated methods.
82*0Sstevel@tonic-gate  * XXX These and other extern's should really move to a icmp header.
83*0Sstevel@tonic-gate  */
84*0Sstevel@tonic-gate extern optdb_obj_t	icmp_opt_obj;
85*0Sstevel@tonic-gate extern uint_t		icmp_max_optsize;
86*0Sstevel@tonic-gate 
87*0Sstevel@tonic-gate /*
88*0Sstevel@tonic-gate  * Synchronization notes:
89*0Sstevel@tonic-gate  *
90*0Sstevel@tonic-gate  * At all points in this code where exclusive access is required, we
91*0Sstevel@tonic-gate  * pass a message to a subroutine by invoking qwriter(..., PERIM_OUTER)
92*0Sstevel@tonic-gate  * which will arrange to call the routine only after all threads have
93*0Sstevel@tonic-gate  * exited the shared resource.
94*0Sstevel@tonic-gate  */
95*0Sstevel@tonic-gate 
96*0Sstevel@tonic-gate /* Named Dispatch Parameter Management Structure */
97*0Sstevel@tonic-gate typedef struct icmpparam_s {
98*0Sstevel@tonic-gate 	uint_t	icmp_param_min;
99*0Sstevel@tonic-gate 	uint_t	icmp_param_max;
100*0Sstevel@tonic-gate 	uint_t	icmp_param_value;
101*0Sstevel@tonic-gate 	char	*icmp_param_name;
102*0Sstevel@tonic-gate } icmpparam_t;
103*0Sstevel@tonic-gate 
104*0Sstevel@tonic-gate static void	icmp_addr_req(queue_t *q, mblk_t *mp);
105*0Sstevel@tonic-gate static void	icmp_bind(queue_t *q, mblk_t *mp);
106*0Sstevel@tonic-gate static void	icmp_bind_proto(queue_t *q);
107*0Sstevel@tonic-gate static int	icmp_build_hdrs(queue_t *q, icmp_t *icmp);
108*0Sstevel@tonic-gate static void	icmp_capability_req(queue_t *q, mblk_t *mp);
109*0Sstevel@tonic-gate static int	icmp_close(queue_t *q);
110*0Sstevel@tonic-gate static void	icmp_connect(queue_t *q, mblk_t *mp);
111*0Sstevel@tonic-gate static void	icmp_disconnect(queue_t *q, mblk_t *mp);
112*0Sstevel@tonic-gate static void	icmp_err_ack(queue_t *q, mblk_t *mp, t_scalar_t t_error,
113*0Sstevel@tonic-gate 		    int sys_error);
114*0Sstevel@tonic-gate static void	icmp_err_ack_prim(queue_t *q, mblk_t *mp, t_scalar_t primitive,
115*0Sstevel@tonic-gate 		    t_scalar_t t_error, int sys_error);
116*0Sstevel@tonic-gate static void	icmp_icmp_error(queue_t *q, mblk_t *mp);
117*0Sstevel@tonic-gate static void	icmp_icmp_error_ipv6(queue_t *q, mblk_t *mp);
118*0Sstevel@tonic-gate static void	icmp_info_req(queue_t *q, mblk_t *mp);
119*0Sstevel@tonic-gate static mblk_t	*icmp_ip_bind_mp(icmp_t *icmp, t_scalar_t bind_prim,
120*0Sstevel@tonic-gate 		    t_scalar_t addr_length, in_port_t);
121*0Sstevel@tonic-gate static int	icmp_open(queue_t *q, dev_t *devp, int flag,
122*0Sstevel@tonic-gate 		    int sflag, cred_t *credp);
123*0Sstevel@tonic-gate static int	icmp_unitdata_opt_process(queue_t *q, mblk_t *mp,
124*0Sstevel@tonic-gate 		    int *errorp, void *thisdg_attrs);
125*0Sstevel@tonic-gate static boolean_t icmp_opt_allow_udr_set(t_scalar_t level, t_scalar_t name);
126*0Sstevel@tonic-gate int		icmp_opt_set(queue_t *q, uint_t optset_context,
127*0Sstevel@tonic-gate 		    int level, int name, uint_t inlen,
128*0Sstevel@tonic-gate 		    uchar_t *invalp, uint_t *outlenp, uchar_t *outvalp,
129*0Sstevel@tonic-gate 		    void *thisdg_attrs, cred_t *cr, mblk_t *mblk);
130*0Sstevel@tonic-gate int		icmp_opt_get(queue_t *q, int level, int name,
131*0Sstevel@tonic-gate 		    uchar_t *ptr);
132*0Sstevel@tonic-gate static int	icmp_param_get(queue_t *q, mblk_t *mp, caddr_t cp, cred_t *cr);
133*0Sstevel@tonic-gate static boolean_t icmp_param_register(icmpparam_t *icmppa, int cnt);
134*0Sstevel@tonic-gate static int	icmp_param_set(queue_t *q, mblk_t *mp, char *value,
135*0Sstevel@tonic-gate 		    caddr_t cp, cred_t *cr);
136*0Sstevel@tonic-gate static int	icmp_pkt_set(uchar_t *invalp, uint_t inlen, boolean_t sticky,
137*0Sstevel@tonic-gate 		    uchar_t **optbufp, uint_t *optlenp);
138*0Sstevel@tonic-gate static void	icmp_rput(queue_t *q, mblk_t *mp);
139*0Sstevel@tonic-gate static void	icmp_rput_bind_ack(queue_t *q, mblk_t *mp);
140*0Sstevel@tonic-gate static int	icmp_snmp_get(queue_t *q, mblk_t *mpctl);
141*0Sstevel@tonic-gate static int	icmp_snmp_set(queue_t *q, t_scalar_t level, t_scalar_t name,
142*0Sstevel@tonic-gate 		    uchar_t *ptr, int len);
143*0Sstevel@tonic-gate static int	icmp_status_report(queue_t *q, mblk_t *mp, caddr_t cp,
144*0Sstevel@tonic-gate 		    cred_t *cr);
145*0Sstevel@tonic-gate static void	icmp_ud_err(queue_t *q, mblk_t *mp, t_scalar_t err);
146*0Sstevel@tonic-gate static void	icmp_unbind(queue_t *q, mblk_t *mp);
147*0Sstevel@tonic-gate static void	icmp_wput(queue_t *q, mblk_t *mp);
148*0Sstevel@tonic-gate static void	icmp_wput_ipv6(queue_t *q, mblk_t *mp, sin6_t *sin6,
149*0Sstevel@tonic-gate 		    t_scalar_t tudr_optlen);
150*0Sstevel@tonic-gate static void	icmp_wput_other(queue_t *q, mblk_t *mp);
151*0Sstevel@tonic-gate static void	icmp_wput_iocdata(queue_t *q, mblk_t *mp);
152*0Sstevel@tonic-gate static void	icmp_wput_restricted(queue_t *q, mblk_t *mp);
153*0Sstevel@tonic-gate 
154*0Sstevel@tonic-gate static void	rawip_kstat_init(void);
155*0Sstevel@tonic-gate static void	rawip_kstat_fini(void);
156*0Sstevel@tonic-gate static int	rawip_kstat_update(kstat_t *kp, int rw);
157*0Sstevel@tonic-gate 
158*0Sstevel@tonic-gate 
159*0Sstevel@tonic-gate static struct module_info info =  {
160*0Sstevel@tonic-gate 	5707, "icmp", 1, INFPSZ, 512, 128
161*0Sstevel@tonic-gate };
162*0Sstevel@tonic-gate 
163*0Sstevel@tonic-gate static struct qinit rinit = {
164*0Sstevel@tonic-gate 	(pfi_t)icmp_rput, NULL, icmp_open, icmp_close, NULL, &info
165*0Sstevel@tonic-gate };
166*0Sstevel@tonic-gate 
167*0Sstevel@tonic-gate static struct qinit winit = {
168*0Sstevel@tonic-gate 	(pfi_t)icmp_wput, NULL, NULL, NULL, NULL, &info
169*0Sstevel@tonic-gate };
170*0Sstevel@tonic-gate 
171*0Sstevel@tonic-gate struct streamtab icmpinfo = {
172*0Sstevel@tonic-gate 	&rinit, &winit
173*0Sstevel@tonic-gate };
174*0Sstevel@tonic-gate 
175*0Sstevel@tonic-gate static sin_t	sin_null;	/* Zero address for quick clears */
176*0Sstevel@tonic-gate static sin6_t	sin6_null;	/* Zero address for quick clears */
177*0Sstevel@tonic-gate static void	*icmp_g_head;	/* Head for list of open icmp streams. */
178*0Sstevel@tonic-gate static IDP	icmp_g_nd;	/* Points to table of ICMP ND variables. */
179*0Sstevel@tonic-gate 
180*0Sstevel@tonic-gate /* MIB-2 stuff for SNMP */
181*0Sstevel@tonic-gate static mib2_rawip_t	rawip_mib;	/* SNMP fixed size info */
182*0Sstevel@tonic-gate static kstat_t		*rawip_mibkp;	/* kstat exporting rawip_mib data */
183*0Sstevel@tonic-gate 
184*0Sstevel@tonic-gate /* Default structure copied into T_INFO_ACK messages */
185*0Sstevel@tonic-gate static struct T_info_ack icmp_g_t_info_ack = {
186*0Sstevel@tonic-gate 	T_INFO_ACK,
187*0Sstevel@tonic-gate 	IP_MAXPACKET,	 /* TSDU_size.  icmp allows maximum size messages. */
188*0Sstevel@tonic-gate 	T_INVALID,	/* ETSDU_size.  icmp does not support expedited data. */
189*0Sstevel@tonic-gate 	T_INVALID,	/* CDATA_size. icmp does not support connect data. */
190*0Sstevel@tonic-gate 	T_INVALID,	/* DDATA_size. icmp does not support disconnect data. */
191*0Sstevel@tonic-gate 	0,		/* ADDR_size - filled in later. */
192*0Sstevel@tonic-gate 	0,		/* OPT_size - not initialized here */
193*0Sstevel@tonic-gate 	IP_MAXPACKET,	/* TIDU_size.  icmp allows maximum size messages. */
194*0Sstevel@tonic-gate 	T_CLTS,		/* SERV_type.  icmp supports connection-less. */
195*0Sstevel@tonic-gate 	TS_UNBND,	/* CURRENT_state.  This is set from icmp_state. */
196*0Sstevel@tonic-gate 	(XPG4_1|SENDZERO) /* PROVIDER_flag */
197*0Sstevel@tonic-gate };
198*0Sstevel@tonic-gate 
199*0Sstevel@tonic-gate /*
200*0Sstevel@tonic-gate  * Table of ND variables supported by icmp.  These are loaded into icmp_g_nd
201*0Sstevel@tonic-gate  * in icmp_open.
202*0Sstevel@tonic-gate  * All of these are alterable, within the min/max values given, at run time.
203*0Sstevel@tonic-gate  */
204*0Sstevel@tonic-gate static icmpparam_t	icmp_param_arr[] = {
205*0Sstevel@tonic-gate 	/* min	max	value	name */
206*0Sstevel@tonic-gate 	{ 0,	128,	32,	"icmp_wroff_extra" },
207*0Sstevel@tonic-gate 	{ 1,	255,	255,	"icmp_ipv4_ttl" },
208*0Sstevel@tonic-gate 	{ 0, IPV6_MAX_HOPS, IPV6_DEFAULT_HOPS,	"icmp_ipv6_hoplimit"},
209*0Sstevel@tonic-gate 	{ 0,	1,	1,	"icmp_bsd_compat" },
210*0Sstevel@tonic-gate 	{ 4096,	65536,	8192,	"icmp_xmit_hiwat"},
211*0Sstevel@tonic-gate 	{ 0,	65536,	1024,	"icmp_xmit_lowat"},
212*0Sstevel@tonic-gate 	{ 4096,	65536,	8192,	"icmp_recv_hiwat"},
213*0Sstevel@tonic-gate 	{ 65536, 1024*1024*1024, 256*1024,	"icmp_max_buf"},
214*0Sstevel@tonic-gate };
215*0Sstevel@tonic-gate #define	icmp_wroff_extra		icmp_param_arr[0].icmp_param_value
216*0Sstevel@tonic-gate #define	icmp_ipv4_ttl			icmp_param_arr[1].icmp_param_value
217*0Sstevel@tonic-gate #define	icmp_ipv6_hoplimit		icmp_param_arr[2].icmp_param_value
218*0Sstevel@tonic-gate #define	icmp_bsd_compat			icmp_param_arr[3].icmp_param_value
219*0Sstevel@tonic-gate #define	icmp_xmit_hiwat			icmp_param_arr[4].icmp_param_value
220*0Sstevel@tonic-gate #define	icmp_xmit_lowat			icmp_param_arr[5].icmp_param_value
221*0Sstevel@tonic-gate #define	icmp_recv_hiwat			icmp_param_arr[6].icmp_param_value
222*0Sstevel@tonic-gate #define	icmp_max_buf			icmp_param_arr[7].icmp_param_value
223*0Sstevel@tonic-gate 
224*0Sstevel@tonic-gate /*
225*0Sstevel@tonic-gate  * This routine is called to handle each O_T_BIND_REQ/T_BIND_REQ message
226*0Sstevel@tonic-gate  * passed to icmp_wput.
227*0Sstevel@tonic-gate  * The O_T_BIND_REQ/T_BIND_REQ is passed downstream to ip with the ICMP
228*0Sstevel@tonic-gate  * protocol type placed in the message following the address. A T_BIND_ACK
229*0Sstevel@tonic-gate  * message is passed upstream when ip acknowledges the request.
230*0Sstevel@tonic-gate  * (Called as writer.)
231*0Sstevel@tonic-gate  */
232*0Sstevel@tonic-gate static void
233*0Sstevel@tonic-gate icmp_bind(queue_t *q, mblk_t *mp)
234*0Sstevel@tonic-gate {
235*0Sstevel@tonic-gate 	sin_t	*sin;
236*0Sstevel@tonic-gate 	sin6_t	*sin6;
237*0Sstevel@tonic-gate 	mblk_t	*mp1;
238*0Sstevel@tonic-gate 	struct T_bind_req	*tbr;
239*0Sstevel@tonic-gate 	icmp_t	*icmp;
240*0Sstevel@tonic-gate 
241*0Sstevel@tonic-gate 	icmp = (icmp_t *)q->q_ptr;
242*0Sstevel@tonic-gate 	if ((mp->b_wptr - mp->b_rptr) < sizeof (*tbr)) {
243*0Sstevel@tonic-gate 		(void) mi_strlog(q, 1, SL_ERROR|SL_TRACE,
244*0Sstevel@tonic-gate 		    "icmp_bind: bad req, len %u",
245*0Sstevel@tonic-gate 		    (uint_t)(mp->b_wptr - mp->b_rptr));
246*0Sstevel@tonic-gate 		icmp_err_ack(q, mp, TPROTO, 0);
247*0Sstevel@tonic-gate 		return;
248*0Sstevel@tonic-gate 	}
249*0Sstevel@tonic-gate 	if (icmp->icmp_state != TS_UNBND) {
250*0Sstevel@tonic-gate 		(void) mi_strlog(q, 1, SL_ERROR|SL_TRACE,
251*0Sstevel@tonic-gate 		    "icmp_bind: bad state, %d", icmp->icmp_state);
252*0Sstevel@tonic-gate 		icmp_err_ack(q, mp, TOUTSTATE, 0);
253*0Sstevel@tonic-gate 		return;
254*0Sstevel@tonic-gate 	}
255*0Sstevel@tonic-gate 	/*
256*0Sstevel@tonic-gate 	 * Reallocate the message to make sure we have enough room for an
257*0Sstevel@tonic-gate 	 * address and the protocol type.
258*0Sstevel@tonic-gate 	 */
259*0Sstevel@tonic-gate 	mp1 = reallocb(mp, sizeof (struct T_bind_ack) + sizeof (sin6_t) + 1, 1);
260*0Sstevel@tonic-gate 	if (!mp1) {
261*0Sstevel@tonic-gate 		icmp_err_ack(q, mp, TSYSERR, ENOMEM);
262*0Sstevel@tonic-gate 		return;
263*0Sstevel@tonic-gate 	}
264*0Sstevel@tonic-gate 	mp = mp1;
265*0Sstevel@tonic-gate 	tbr = (struct T_bind_req *)mp->b_rptr;
266*0Sstevel@tonic-gate 	switch (tbr->ADDR_length) {
267*0Sstevel@tonic-gate 	case 0:			/* Generic request */
268*0Sstevel@tonic-gate 		tbr->ADDR_offset = sizeof (struct T_bind_req);
269*0Sstevel@tonic-gate 		if (icmp->icmp_family == AF_INET) {
270*0Sstevel@tonic-gate 			tbr->ADDR_length = sizeof (sin_t);
271*0Sstevel@tonic-gate 			sin = (sin_t *)&tbr[1];
272*0Sstevel@tonic-gate 			*sin = sin_null;
273*0Sstevel@tonic-gate 			sin->sin_family = AF_INET;
274*0Sstevel@tonic-gate 			mp->b_wptr = (uchar_t *)&sin[1];
275*0Sstevel@tonic-gate 		} else {
276*0Sstevel@tonic-gate 			ASSERT(icmp->icmp_family == AF_INET6);
277*0Sstevel@tonic-gate 			tbr->ADDR_length = sizeof (sin6_t);
278*0Sstevel@tonic-gate 			sin6 = (sin6_t *)&tbr[1];
279*0Sstevel@tonic-gate 			*sin6 = sin6_null;
280*0Sstevel@tonic-gate 			sin6->sin6_family = AF_INET6;
281*0Sstevel@tonic-gate 			mp->b_wptr = (uchar_t *)&sin6[1];
282*0Sstevel@tonic-gate 		}
283*0Sstevel@tonic-gate 		break;
284*0Sstevel@tonic-gate 	case sizeof (sin_t):	/* Complete IP address */
285*0Sstevel@tonic-gate 		sin = (sin_t *)mi_offset_param(mp, tbr->ADDR_offset,
286*0Sstevel@tonic-gate 		    sizeof (sin_t));
287*0Sstevel@tonic-gate 		if (sin == NULL || !OK_32PTR((char *)sin)) {
288*0Sstevel@tonic-gate 			icmp_err_ack(q, mp, TSYSERR, EINVAL);
289*0Sstevel@tonic-gate 			return;
290*0Sstevel@tonic-gate 		}
291*0Sstevel@tonic-gate 		if (icmp->icmp_family != AF_INET ||
292*0Sstevel@tonic-gate 		    sin->sin_family != AF_INET) {
293*0Sstevel@tonic-gate 			icmp_err_ack(q, mp, TSYSERR, EAFNOSUPPORT);
294*0Sstevel@tonic-gate 			return;
295*0Sstevel@tonic-gate 		}
296*0Sstevel@tonic-gate 		break;
297*0Sstevel@tonic-gate 	case sizeof (sin6_t):	/* Complete IP address */
298*0Sstevel@tonic-gate 		sin6 = (sin6_t *)mi_offset_param(mp, tbr->ADDR_offset,
299*0Sstevel@tonic-gate 		    sizeof (sin6_t));
300*0Sstevel@tonic-gate 		if (sin6 == NULL || !OK_32PTR((char *)sin6)) {
301*0Sstevel@tonic-gate 			icmp_err_ack(q, mp, TSYSERR, EINVAL);
302*0Sstevel@tonic-gate 			return;
303*0Sstevel@tonic-gate 		}
304*0Sstevel@tonic-gate 		if (icmp->icmp_family != AF_INET6 ||
305*0Sstevel@tonic-gate 		    sin6->sin6_family != AF_INET6) {
306*0Sstevel@tonic-gate 			icmp_err_ack(q, mp, TSYSERR, EAFNOSUPPORT);
307*0Sstevel@tonic-gate 			return;
308*0Sstevel@tonic-gate 		}
309*0Sstevel@tonic-gate 		/* No support for mapped addresses on raw sockets */
310*0Sstevel@tonic-gate 		if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
311*0Sstevel@tonic-gate 			icmp_err_ack(q, mp, TSYSERR, EADDRNOTAVAIL);
312*0Sstevel@tonic-gate 			return;
313*0Sstevel@tonic-gate 		}
314*0Sstevel@tonic-gate 		break;
315*0Sstevel@tonic-gate 	default:
316*0Sstevel@tonic-gate 		(void) mi_strlog(q, 1, SL_ERROR|SL_TRACE,
317*0Sstevel@tonic-gate 		    "icmp_bind: bad ADDR_length %d", tbr->ADDR_length);
318*0Sstevel@tonic-gate 		icmp_err_ack(q, mp, TBADADDR, 0);
319*0Sstevel@tonic-gate 		return;
320*0Sstevel@tonic-gate 	}
321*0Sstevel@tonic-gate 	/*
322*0Sstevel@tonic-gate 	 * Copy the source address into our icmp structure.  This address
323*0Sstevel@tonic-gate 	 * may still be zero; if so, ip will fill in the correct address
324*0Sstevel@tonic-gate 	 * each time an outbound packet is passed to it.
325*0Sstevel@tonic-gate 	 * If we are binding to a broadcast or multicast address icmp_rput
326*0Sstevel@tonic-gate 	 * will clear the source address when it receives the T_BIND_ACK.
327*0Sstevel@tonic-gate 	 */
328*0Sstevel@tonic-gate 	icmp->icmp_state = TS_IDLE;
329*0Sstevel@tonic-gate 
330*0Sstevel@tonic-gate 	if (icmp->icmp_family == AF_INET) {
331*0Sstevel@tonic-gate 		ASSERT(sin != NULL);
332*0Sstevel@tonic-gate 		ASSERT(icmp->icmp_ipversion == IPV4_VERSION);
333*0Sstevel@tonic-gate 		IN6_IPADDR_TO_V4MAPPED(sin->sin_addr.s_addr,
334*0Sstevel@tonic-gate 		    &icmp->icmp_v6src);
335*0Sstevel@tonic-gate 		icmp->icmp_max_hdr_len = IP_SIMPLE_HDR_LENGTH +
336*0Sstevel@tonic-gate 		    icmp->icmp_ip_snd_options_len;
337*0Sstevel@tonic-gate 		icmp->icmp_bound_v6src = icmp->icmp_v6src;
338*0Sstevel@tonic-gate 	} else {
339*0Sstevel@tonic-gate 		int error;
340*0Sstevel@tonic-gate 
341*0Sstevel@tonic-gate 		ASSERT(sin6 != NULL);
342*0Sstevel@tonic-gate 		ASSERT(icmp->icmp_ipversion == IPV6_VERSION);
343*0Sstevel@tonic-gate 		icmp->icmp_v6src = sin6->sin6_addr;
344*0Sstevel@tonic-gate 		icmp->icmp_max_hdr_len = icmp->icmp_sticky_hdrs_len;
345*0Sstevel@tonic-gate 		icmp->icmp_bound_v6src = icmp->icmp_v6src;
346*0Sstevel@tonic-gate 
347*0Sstevel@tonic-gate 		/* Rebuild the header template */
348*0Sstevel@tonic-gate 		error = icmp_build_hdrs(q, icmp);
349*0Sstevel@tonic-gate 		if (error != 0) {
350*0Sstevel@tonic-gate 			icmp_err_ack(q, mp, TSYSERR, error);
351*0Sstevel@tonic-gate 			return;
352*0Sstevel@tonic-gate 		}
353*0Sstevel@tonic-gate 	}
354*0Sstevel@tonic-gate 	/*
355*0Sstevel@tonic-gate 	 * Place protocol type in the O_T_BIND_REQ/T_BIND_REQ following
356*0Sstevel@tonic-gate 	 * the address.
357*0Sstevel@tonic-gate 	 */
358*0Sstevel@tonic-gate 	*mp->b_wptr++ = icmp->icmp_proto;
359*0Sstevel@tonic-gate 	if (!(V6_OR_V4_INADDR_ANY(icmp->icmp_v6src))) {
360*0Sstevel@tonic-gate 		/*
361*0Sstevel@tonic-gate 		 * Append a request for an IRE if src not 0 (INADDR_ANY)
362*0Sstevel@tonic-gate 		 */
363*0Sstevel@tonic-gate 		mp->b_cont = allocb(sizeof (ire_t), BPRI_HI);
364*0Sstevel@tonic-gate 		if (!mp->b_cont) {
365*0Sstevel@tonic-gate 			icmp_err_ack(q, mp, TSYSERR, ENOMEM);
366*0Sstevel@tonic-gate 			return;
367*0Sstevel@tonic-gate 		}
368*0Sstevel@tonic-gate 		mp->b_cont->b_wptr += sizeof (ire_t);
369*0Sstevel@tonic-gate 		mp->b_cont->b_datap->db_type = IRE_DB_REQ_TYPE;
370*0Sstevel@tonic-gate 	}
371*0Sstevel@tonic-gate 
372*0Sstevel@tonic-gate 	/* Pass the O_T_BIND_REQ/T_BIND_REQ to ip. */
373*0Sstevel@tonic-gate 	putnext(q, mp);
374*0Sstevel@tonic-gate }
375*0Sstevel@tonic-gate 
376*0Sstevel@tonic-gate /*
377*0Sstevel@tonic-gate  * Send message to IP to just bind to the protocol.
378*0Sstevel@tonic-gate  */
379*0Sstevel@tonic-gate static void
380*0Sstevel@tonic-gate icmp_bind_proto(queue_t *q)
381*0Sstevel@tonic-gate {
382*0Sstevel@tonic-gate 	mblk_t	*mp;
383*0Sstevel@tonic-gate 	struct T_bind_req	*tbr;
384*0Sstevel@tonic-gate 	icmp_t	*icmp;
385*0Sstevel@tonic-gate 
386*0Sstevel@tonic-gate 	icmp = (icmp_t *)q->q_ptr;
387*0Sstevel@tonic-gate 	mp = allocb(sizeof (struct T_bind_req) + sizeof (sin6_t) + 1,
388*0Sstevel@tonic-gate 	    BPRI_MED);
389*0Sstevel@tonic-gate 	if (!mp) {
390*0Sstevel@tonic-gate 		return;
391*0Sstevel@tonic-gate 	}
392*0Sstevel@tonic-gate 	mp->b_datap->db_type = M_PROTO;
393*0Sstevel@tonic-gate 	tbr = (struct T_bind_req *)mp->b_rptr;
394*0Sstevel@tonic-gate 	tbr->PRIM_type = O_T_BIND_REQ; /* change to T_BIND_REQ ? */
395*0Sstevel@tonic-gate 	tbr->ADDR_offset = sizeof (struct T_bind_req);
396*0Sstevel@tonic-gate 	if (icmp->icmp_ipversion == IPV4_VERSION) {
397*0Sstevel@tonic-gate 		sin_t	*sin;
398*0Sstevel@tonic-gate 
399*0Sstevel@tonic-gate 		tbr->ADDR_length = sizeof (sin_t);
400*0Sstevel@tonic-gate 		sin = (sin_t *)&tbr[1];
401*0Sstevel@tonic-gate 		*sin = sin_null;
402*0Sstevel@tonic-gate 		sin->sin_family = AF_INET;
403*0Sstevel@tonic-gate 		mp->b_wptr = (uchar_t *)&sin[1];
404*0Sstevel@tonic-gate 	} else {
405*0Sstevel@tonic-gate 		sin6_t	*sin6;
406*0Sstevel@tonic-gate 
407*0Sstevel@tonic-gate 		ASSERT(icmp->icmp_ipversion == IPV6_VERSION);
408*0Sstevel@tonic-gate 		tbr->ADDR_length = sizeof (sin6_t);
409*0Sstevel@tonic-gate 		sin6 = (sin6_t *)&tbr[1];
410*0Sstevel@tonic-gate 		*sin6 = sin6_null;
411*0Sstevel@tonic-gate 		sin6->sin6_family = AF_INET6;
412*0Sstevel@tonic-gate 		mp->b_wptr = (uchar_t *)&sin6[1];
413*0Sstevel@tonic-gate 	}
414*0Sstevel@tonic-gate 
415*0Sstevel@tonic-gate 	/* Place protocol type in the O_T_BIND_REQ following the address. */
416*0Sstevel@tonic-gate 	*mp->b_wptr++ = icmp->icmp_proto;
417*0Sstevel@tonic-gate 
418*0Sstevel@tonic-gate 	/* Pass the O_T_BIND_REQ to ip. */
419*0Sstevel@tonic-gate 	putnext(q, mp);
420*0Sstevel@tonic-gate }
421*0Sstevel@tonic-gate 
422*0Sstevel@tonic-gate /*
423*0Sstevel@tonic-gate  * This routine handles each T_CONN_REQ message passed to icmp.  It
424*0Sstevel@tonic-gate  * associates a default destination address with the stream.
425*0Sstevel@tonic-gate  *
426*0Sstevel@tonic-gate  * This routine sends down a T_BIND_REQ to IP with the following mblks:
427*0Sstevel@tonic-gate  *	T_BIND_REQ	- specifying local and remote address.
428*0Sstevel@tonic-gate  *	IRE_DB_REQ_TYPE	- to get an IRE back containing ire_type and src
429*0Sstevel@tonic-gate  *	T_OK_ACK	- for the T_CONN_REQ
430*0Sstevel@tonic-gate  *	T_CONN_CON	- to keep the TPI user happy
431*0Sstevel@tonic-gate  *
432*0Sstevel@tonic-gate  * The connect completes in icmp_rput.
433*0Sstevel@tonic-gate  * When a T_BIND_ACK is received information is extracted from the IRE
434*0Sstevel@tonic-gate  * and the two appended messages are sent to the TPI user.
435*0Sstevel@tonic-gate  * Should icmp_rput receive T_ERROR_ACK for the T_BIND_REQ it will convert
436*0Sstevel@tonic-gate  * it to an error ack for the appropriate primitive.
437*0Sstevel@tonic-gate  */
438*0Sstevel@tonic-gate static void
439*0Sstevel@tonic-gate icmp_connect(queue_t *q, mblk_t *mp)
440*0Sstevel@tonic-gate {
441*0Sstevel@tonic-gate 	sin_t	*sin;
442*0Sstevel@tonic-gate 	sin6_t	*sin6;
443*0Sstevel@tonic-gate 	mblk_t	*mp1, *mp2;
444*0Sstevel@tonic-gate 	struct T_conn_req	*tcr;
445*0Sstevel@tonic-gate 	icmp_t	*icmp;
446*0Sstevel@tonic-gate 	ipaddr_t	v4dst;
447*0Sstevel@tonic-gate 	in6_addr_t	v6dst;
448*0Sstevel@tonic-gate 	uint32_t	flowinfo;
449*0Sstevel@tonic-gate 
450*0Sstevel@tonic-gate 	icmp = (icmp_t *)q->q_ptr;
451*0Sstevel@tonic-gate 	tcr = (struct T_conn_req *)mp->b_rptr;
452*0Sstevel@tonic-gate 	/* Sanity checks */
453*0Sstevel@tonic-gate 	if ((mp->b_wptr - mp->b_rptr < sizeof (struct T_conn_req))) {
454*0Sstevel@tonic-gate 		icmp_err_ack(q, mp, TPROTO, 0);
455*0Sstevel@tonic-gate 		return;
456*0Sstevel@tonic-gate 	}
457*0Sstevel@tonic-gate 
458*0Sstevel@tonic-gate 	if (icmp->icmp_state == TS_DATA_XFER) {
459*0Sstevel@tonic-gate 		/* Already connected - clear out state */
460*0Sstevel@tonic-gate 		icmp->icmp_v6src = icmp->icmp_bound_v6src;
461*0Sstevel@tonic-gate 		icmp->icmp_state = TS_IDLE;
462*0Sstevel@tonic-gate 	}
463*0Sstevel@tonic-gate 
464*0Sstevel@tonic-gate 
465*0Sstevel@tonic-gate 	if (tcr->OPT_length != 0) {
466*0Sstevel@tonic-gate 		icmp_err_ack(q, mp, TBADOPT, 0);
467*0Sstevel@tonic-gate 		return;
468*0Sstevel@tonic-gate 	}
469*0Sstevel@tonic-gate 	switch (tcr->DEST_length) {
470*0Sstevel@tonic-gate 	default:
471*0Sstevel@tonic-gate 		icmp_err_ack(q, mp, TBADADDR, 0);
472*0Sstevel@tonic-gate 		return;
473*0Sstevel@tonic-gate 
474*0Sstevel@tonic-gate 	case sizeof (sin_t):
475*0Sstevel@tonic-gate 		sin = (sin_t *)mi_offset_param(mp, tcr->DEST_offset,
476*0Sstevel@tonic-gate 		    sizeof (sin_t));
477*0Sstevel@tonic-gate 		if (sin == NULL || !OK_32PTR((char *)sin)) {
478*0Sstevel@tonic-gate 			icmp_err_ack(q, mp, TSYSERR, EINVAL);
479*0Sstevel@tonic-gate 			return;
480*0Sstevel@tonic-gate 		}
481*0Sstevel@tonic-gate 		if (icmp->icmp_family != AF_INET ||
482*0Sstevel@tonic-gate 		    sin->sin_family != AF_INET) {
483*0Sstevel@tonic-gate 			icmp_err_ack(q, mp, TSYSERR, EAFNOSUPPORT);
484*0Sstevel@tonic-gate 			return;
485*0Sstevel@tonic-gate 		}
486*0Sstevel@tonic-gate 		v4dst = sin->sin_addr.s_addr;
487*0Sstevel@tonic-gate 		IN6_IPADDR_TO_V4MAPPED(v4dst, &v6dst);
488*0Sstevel@tonic-gate 		ASSERT(icmp->icmp_ipversion == IPV4_VERSION);
489*0Sstevel@tonic-gate 		icmp->icmp_max_hdr_len = IP_SIMPLE_HDR_LENGTH +
490*0Sstevel@tonic-gate 		    icmp->icmp_ip_snd_options_len;
491*0Sstevel@tonic-gate 		break;
492*0Sstevel@tonic-gate 
493*0Sstevel@tonic-gate 	case sizeof (sin6_t):
494*0Sstevel@tonic-gate 		sin6 = (sin6_t *)mi_offset_param(mp, tcr->DEST_offset,
495*0Sstevel@tonic-gate 		    sizeof (sin6_t));
496*0Sstevel@tonic-gate 		if (sin6 == NULL || !OK_32PTR((char *)sin6)) {
497*0Sstevel@tonic-gate 			icmp_err_ack(q, mp, TSYSERR, EINVAL);
498*0Sstevel@tonic-gate 			return;
499*0Sstevel@tonic-gate 		}
500*0Sstevel@tonic-gate 		if (icmp->icmp_family != AF_INET6 ||
501*0Sstevel@tonic-gate 		    sin6->sin6_family != AF_INET6) {
502*0Sstevel@tonic-gate 			icmp_err_ack(q, mp, TSYSERR, EAFNOSUPPORT);
503*0Sstevel@tonic-gate 			return;
504*0Sstevel@tonic-gate 		}
505*0Sstevel@tonic-gate 		/* No support for mapped addresses on raw sockets */
506*0Sstevel@tonic-gate 		if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
507*0Sstevel@tonic-gate 			icmp_err_ack(q, mp, TSYSERR, EADDRNOTAVAIL);
508*0Sstevel@tonic-gate 			return;
509*0Sstevel@tonic-gate 		}
510*0Sstevel@tonic-gate 		v6dst = sin6->sin6_addr;
511*0Sstevel@tonic-gate 		ASSERT(icmp->icmp_ipversion == IPV6_VERSION);
512*0Sstevel@tonic-gate 		icmp->icmp_max_hdr_len = icmp->icmp_sticky_hdrs_len;
513*0Sstevel@tonic-gate 		flowinfo = sin6->sin6_flowinfo;
514*0Sstevel@tonic-gate 		break;
515*0Sstevel@tonic-gate 	}
516*0Sstevel@tonic-gate 	if (icmp->icmp_ipversion == IPV4_VERSION) {
517*0Sstevel@tonic-gate 		/*
518*0Sstevel@tonic-gate 		 * Interpret a zero destination to mean loopback.
519*0Sstevel@tonic-gate 		 * Update the T_CONN_REQ (sin/sin6) since it is used to
520*0Sstevel@tonic-gate 		 * generate the T_CONN_CON.
521*0Sstevel@tonic-gate 		 */
522*0Sstevel@tonic-gate 		if (v4dst == INADDR_ANY) {
523*0Sstevel@tonic-gate 			v4dst = htonl(INADDR_LOOPBACK);
524*0Sstevel@tonic-gate 			IN6_IPADDR_TO_V4MAPPED(v4dst, &v6dst);
525*0Sstevel@tonic-gate 			if (icmp->icmp_family == AF_INET) {
526*0Sstevel@tonic-gate 				sin->sin_addr.s_addr = v4dst;
527*0Sstevel@tonic-gate 			} else {
528*0Sstevel@tonic-gate 				sin6->sin6_addr = v6dst;
529*0Sstevel@tonic-gate 			}
530*0Sstevel@tonic-gate 		}
531*0Sstevel@tonic-gate 		icmp->icmp_v6dst = v6dst;
532*0Sstevel@tonic-gate 		icmp->icmp_flowinfo = 0;
533*0Sstevel@tonic-gate 
534*0Sstevel@tonic-gate 		/*
535*0Sstevel@tonic-gate 		 * If the destination address is multicast and
536*0Sstevel@tonic-gate 		 * an outgoing multicast interface has been set,
537*0Sstevel@tonic-gate 		 * use the address of that interface as our
538*0Sstevel@tonic-gate 		 * source address if no source address has been set.
539*0Sstevel@tonic-gate 		 */
540*0Sstevel@tonic-gate 		if (V4_PART_OF_V6(icmp->icmp_v6src) == INADDR_ANY &&
541*0Sstevel@tonic-gate 		    CLASSD(v4dst) &&
542*0Sstevel@tonic-gate 		    icmp->icmp_multicast_if_addr != INADDR_ANY) {
543*0Sstevel@tonic-gate 			IN6_IPADDR_TO_V4MAPPED(icmp->icmp_multicast_if_addr,
544*0Sstevel@tonic-gate 			    &icmp->icmp_v6src);
545*0Sstevel@tonic-gate 		}
546*0Sstevel@tonic-gate 	} else {
547*0Sstevel@tonic-gate 		ASSERT(icmp->icmp_ipversion == IPV6_VERSION);
548*0Sstevel@tonic-gate 		/*
549*0Sstevel@tonic-gate 		 * Interpret a zero destination to mean loopback.
550*0Sstevel@tonic-gate 		 * Update the T_CONN_REQ (sin/sin6) since it is used to
551*0Sstevel@tonic-gate 		 * generate the T_CONN_CON.
552*0Sstevel@tonic-gate 		 */
553*0Sstevel@tonic-gate 		if (IN6_IS_ADDR_UNSPECIFIED(&v6dst)) {
554*0Sstevel@tonic-gate 			v6dst = ipv6_loopback;
555*0Sstevel@tonic-gate 			sin6->sin6_addr = v6dst;
556*0Sstevel@tonic-gate 		}
557*0Sstevel@tonic-gate 		icmp->icmp_v6dst = v6dst;
558*0Sstevel@tonic-gate 		icmp->icmp_flowinfo = flowinfo;
559*0Sstevel@tonic-gate 		/*
560*0Sstevel@tonic-gate 		 * If the destination address is multicast and
561*0Sstevel@tonic-gate 		 * an outgoing multicast interface has been set,
562*0Sstevel@tonic-gate 		 * then the ip bind logic will pick the correct source
563*0Sstevel@tonic-gate 		 * address (i.e. matching the outgoing multicast interface).
564*0Sstevel@tonic-gate 		 */
565*0Sstevel@tonic-gate 	}
566*0Sstevel@tonic-gate 
567*0Sstevel@tonic-gate 	/*
568*0Sstevel@tonic-gate 	 * Send down bind to IP to verify that there is a route
569*0Sstevel@tonic-gate 	 * and to determine the source address.
570*0Sstevel@tonic-gate 	 * This will come back as T_BIND_ACK with an IRE_DB_TYPE in rput.
571*0Sstevel@tonic-gate 	 */
572*0Sstevel@tonic-gate 	if (icmp->icmp_family == AF_INET) {
573*0Sstevel@tonic-gate 		mp1 = icmp_ip_bind_mp(icmp, O_T_BIND_REQ, sizeof (ipa_conn_t),
574*0Sstevel@tonic-gate 		    sin->sin_port);
575*0Sstevel@tonic-gate 	} else {
576*0Sstevel@tonic-gate 		ASSERT(icmp->icmp_family == AF_INET6);
577*0Sstevel@tonic-gate 		mp1 = icmp_ip_bind_mp(icmp, O_T_BIND_REQ, sizeof (ipa6_conn_t),
578*0Sstevel@tonic-gate 		    sin6->sin6_port);
579*0Sstevel@tonic-gate 	}
580*0Sstevel@tonic-gate 	if (mp1 == NULL) {
581*0Sstevel@tonic-gate 		icmp_err_ack(q, mp, TSYSERR, ENOMEM);
582*0Sstevel@tonic-gate 		return;
583*0Sstevel@tonic-gate 	}
584*0Sstevel@tonic-gate 
585*0Sstevel@tonic-gate 	/*
586*0Sstevel@tonic-gate 	 * We also have to send a connection confirmation to
587*0Sstevel@tonic-gate 	 * keep TLI happy. Prepare it for icmp_rput.
588*0Sstevel@tonic-gate 	 */
589*0Sstevel@tonic-gate 	if (icmp->icmp_family == AF_INET) {
590*0Sstevel@tonic-gate 		mp2 = mi_tpi_conn_con(NULL, (char *)sin, sizeof (*sin), NULL,
591*0Sstevel@tonic-gate 		    0);
592*0Sstevel@tonic-gate 	} else {
593*0Sstevel@tonic-gate 		ASSERT(icmp->icmp_family == AF_INET6);
594*0Sstevel@tonic-gate 		mp2 = mi_tpi_conn_con(NULL, (char *)sin6, sizeof (*sin6), NULL,
595*0Sstevel@tonic-gate 		    0);
596*0Sstevel@tonic-gate 	}
597*0Sstevel@tonic-gate 	if (mp2 == NULL) {
598*0Sstevel@tonic-gate 		freemsg(mp1);
599*0Sstevel@tonic-gate 		icmp_err_ack(q, mp, TSYSERR, ENOMEM);
600*0Sstevel@tonic-gate 		return;
601*0Sstevel@tonic-gate 	}
602*0Sstevel@tonic-gate 
603*0Sstevel@tonic-gate 	mp = mi_tpi_ok_ack_alloc(mp);
604*0Sstevel@tonic-gate 	if (mp == NULL) {
605*0Sstevel@tonic-gate 		/* Unable to reuse the T_CONN_REQ for the ack. */
606*0Sstevel@tonic-gate 		freemsg(mp2);
607*0Sstevel@tonic-gate 		icmp_err_ack_prim(q, mp1, T_CONN_REQ, TSYSERR, ENOMEM);
608*0Sstevel@tonic-gate 		return;
609*0Sstevel@tonic-gate 	}
610*0Sstevel@tonic-gate 
611*0Sstevel@tonic-gate 	icmp->icmp_state = TS_DATA_XFER;
612*0Sstevel@tonic-gate 
613*0Sstevel@tonic-gate 	/* Hang onto the T_OK_ACK and T_CONN_CON for later. */
614*0Sstevel@tonic-gate 	linkb(mp1, mp);
615*0Sstevel@tonic-gate 	linkb(mp1, mp2);
616*0Sstevel@tonic-gate 
617*0Sstevel@tonic-gate 	putnext(q, mp1);
618*0Sstevel@tonic-gate }
619*0Sstevel@tonic-gate 
620*0Sstevel@tonic-gate static int
621*0Sstevel@tonic-gate icmp_close(queue_t *q)
622*0Sstevel@tonic-gate {
623*0Sstevel@tonic-gate 	icmp_t	*icmp = (icmp_t *)q->q_ptr;
624*0Sstevel@tonic-gate 	int	i1;
625*0Sstevel@tonic-gate 
626*0Sstevel@tonic-gate 	qprocsoff(q);
627*0Sstevel@tonic-gate 
628*0Sstevel@tonic-gate 	/* If there are any options associated with the stream, free them. */
629*0Sstevel@tonic-gate 	if (icmp->icmp_ip_snd_options)
630*0Sstevel@tonic-gate 		mi_free((char *)icmp->icmp_ip_snd_options);
631*0Sstevel@tonic-gate 
632*0Sstevel@tonic-gate 	if (icmp->icmp_filter != NULL)
633*0Sstevel@tonic-gate 		kmem_free(icmp->icmp_filter, sizeof (icmp6_filter_t));
634*0Sstevel@tonic-gate 
635*0Sstevel@tonic-gate 	/* Free memory associated with sticky options */
636*0Sstevel@tonic-gate 	if (icmp->icmp_sticky_hdrs_len != 0) {
637*0Sstevel@tonic-gate 		kmem_free(icmp->icmp_sticky_hdrs,
638*0Sstevel@tonic-gate 		    icmp->icmp_sticky_hdrs_len);
639*0Sstevel@tonic-gate 		icmp->icmp_sticky_hdrs = NULL;
640*0Sstevel@tonic-gate 		icmp->icmp_sticky_hdrs_len = 0;
641*0Sstevel@tonic-gate 	}
642*0Sstevel@tonic-gate 	if (icmp->icmp_sticky_ipp.ipp_fields & IPPF_HOPOPTS) {
643*0Sstevel@tonic-gate 		kmem_free(icmp->icmp_sticky_ipp.ipp_hopopts,
644*0Sstevel@tonic-gate 		    icmp->icmp_sticky_ipp.ipp_hopoptslen);
645*0Sstevel@tonic-gate 	}
646*0Sstevel@tonic-gate 	if (icmp->icmp_sticky_ipp.ipp_fields & IPPF_RTDSTOPTS) {
647*0Sstevel@tonic-gate 		kmem_free(icmp->icmp_sticky_ipp.ipp_rtdstopts,
648*0Sstevel@tonic-gate 		    icmp->icmp_sticky_ipp.ipp_rtdstoptslen);
649*0Sstevel@tonic-gate 	}
650*0Sstevel@tonic-gate 	if (icmp->icmp_sticky_ipp.ipp_fields & IPPF_RTHDR) {
651*0Sstevel@tonic-gate 		kmem_free(icmp->icmp_sticky_ipp.ipp_rthdr,
652*0Sstevel@tonic-gate 		    icmp->icmp_sticky_ipp.ipp_rthdrlen);
653*0Sstevel@tonic-gate 	}
654*0Sstevel@tonic-gate 	if (icmp->icmp_sticky_ipp.ipp_fields & IPPF_DSTOPTS) {
655*0Sstevel@tonic-gate 		kmem_free(icmp->icmp_sticky_ipp.ipp_dstopts,
656*0Sstevel@tonic-gate 		    icmp->icmp_sticky_ipp.ipp_dstoptslen);
657*0Sstevel@tonic-gate 	}
658*0Sstevel@tonic-gate 	if (icmp->icmp_sticky_ipp.ipp_fields & IPPF_PATHMTU) {
659*0Sstevel@tonic-gate 		kmem_free(icmp->icmp_sticky_ipp.ipp_pathmtu,
660*0Sstevel@tonic-gate 		    icmp->icmp_sticky_ipp.ipp_pathmtulen);
661*0Sstevel@tonic-gate 	}
662*0Sstevel@tonic-gate 	icmp->icmp_sticky_ipp.ipp_fields &=
663*0Sstevel@tonic-gate 	    ~(IPPF_HOPOPTS|IPPF_RTDSTOPTS|IPPF_RTHDR|IPPF_DSTOPTS);
664*0Sstevel@tonic-gate 
665*0Sstevel@tonic-gate 	crfree(icmp->icmp_credp);
666*0Sstevel@tonic-gate 
667*0Sstevel@tonic-gate 	/* Free the icmp structure and release the minor device number. */
668*0Sstevel@tonic-gate 	i1 = mi_close_comm(&icmp_g_head, q);
669*0Sstevel@tonic-gate 
670*0Sstevel@tonic-gate 	return (i1);
671*0Sstevel@tonic-gate }
672*0Sstevel@tonic-gate 
673*0Sstevel@tonic-gate /*
674*0Sstevel@tonic-gate  * This routine handles each T_DISCON_REQ message passed to icmp
675*0Sstevel@tonic-gate  * as an indicating that ICMP is no longer connected. This results
676*0Sstevel@tonic-gate  * in sending a T_BIND_REQ to IP to restore the binding to just
677*0Sstevel@tonic-gate  * the local address.
678*0Sstevel@tonic-gate  *
679*0Sstevel@tonic-gate  * This routine sends down a T_BIND_REQ to IP with the following mblks:
680*0Sstevel@tonic-gate  *	T_BIND_REQ	- specifying just the local address.
681*0Sstevel@tonic-gate  *	T_OK_ACK	- for the T_DISCON_REQ
682*0Sstevel@tonic-gate  *
683*0Sstevel@tonic-gate  * The disconnect completes in icmp_rput.
684*0Sstevel@tonic-gate  * When a T_BIND_ACK is received the appended T_OK_ACK is sent to the TPI user.
685*0Sstevel@tonic-gate  * Should icmp_rput receive T_ERROR_ACK for the T_BIND_REQ it will convert
686*0Sstevel@tonic-gate  * it to an error ack for the appropriate primitive.
687*0Sstevel@tonic-gate  */
688*0Sstevel@tonic-gate static void
689*0Sstevel@tonic-gate icmp_disconnect(queue_t *q, mblk_t *mp)
690*0Sstevel@tonic-gate {
691*0Sstevel@tonic-gate 	icmp_t	*icmp;
692*0Sstevel@tonic-gate 	mblk_t	*mp1;
693*0Sstevel@tonic-gate 
694*0Sstevel@tonic-gate 	icmp = (icmp_t *)q->q_ptr;
695*0Sstevel@tonic-gate 
696*0Sstevel@tonic-gate 	if (icmp->icmp_state != TS_DATA_XFER) {
697*0Sstevel@tonic-gate 		(void) mi_strlog(q, 1, SL_ERROR|SL_TRACE,
698*0Sstevel@tonic-gate 		    "icmp_disconnect: bad state, %d", icmp->icmp_state);
699*0Sstevel@tonic-gate 		icmp_err_ack(q, mp, TOUTSTATE, 0);
700*0Sstevel@tonic-gate 		return;
701*0Sstevel@tonic-gate 	}
702*0Sstevel@tonic-gate 	icmp->icmp_v6src = icmp->icmp_bound_v6src;
703*0Sstevel@tonic-gate 	icmp->icmp_state = TS_IDLE;
704*0Sstevel@tonic-gate 
705*0Sstevel@tonic-gate 	/*
706*0Sstevel@tonic-gate 	 * Send down bind to IP to remove the full binding and revert
707*0Sstevel@tonic-gate 	 * to the local address binding.
708*0Sstevel@tonic-gate 	 */
709*0Sstevel@tonic-gate 	if (icmp->icmp_family == AF_INET) {
710*0Sstevel@tonic-gate 		mp1 = icmp_ip_bind_mp(icmp, O_T_BIND_REQ, sizeof (sin_t), 0);
711*0Sstevel@tonic-gate 	} else {
712*0Sstevel@tonic-gate 		ASSERT(icmp->icmp_family == AF_INET6);
713*0Sstevel@tonic-gate 		mp1 = icmp_ip_bind_mp(icmp, O_T_BIND_REQ, sizeof (sin6_t), 0);
714*0Sstevel@tonic-gate 	}
715*0Sstevel@tonic-gate 	if (mp1 == NULL) {
716*0Sstevel@tonic-gate 		icmp_err_ack(q, mp, TSYSERR, ENOMEM);
717*0Sstevel@tonic-gate 		return;
718*0Sstevel@tonic-gate 	}
719*0Sstevel@tonic-gate 	mp = mi_tpi_ok_ack_alloc(mp);
720*0Sstevel@tonic-gate 	if (mp == NULL) {
721*0Sstevel@tonic-gate 		/* Unable to reuse the T_DISCON_REQ for the ack. */
722*0Sstevel@tonic-gate 		icmp_err_ack_prim(q, mp1, T_DISCON_REQ, TSYSERR, ENOMEM);
723*0Sstevel@tonic-gate 		return;
724*0Sstevel@tonic-gate 	}
725*0Sstevel@tonic-gate 
726*0Sstevel@tonic-gate 	if (icmp->icmp_family == AF_INET6) {
727*0Sstevel@tonic-gate 		int error;
728*0Sstevel@tonic-gate 
729*0Sstevel@tonic-gate 		/* Rebuild the header template */
730*0Sstevel@tonic-gate 		error = icmp_build_hdrs(q, icmp);
731*0Sstevel@tonic-gate 		if (error != 0) {
732*0Sstevel@tonic-gate 			icmp_err_ack_prim(q, mp, T_DISCON_REQ, TSYSERR, error);
733*0Sstevel@tonic-gate 			freemsg(mp1);
734*0Sstevel@tonic-gate 			return;
735*0Sstevel@tonic-gate 		}
736*0Sstevel@tonic-gate 	}
737*0Sstevel@tonic-gate 	icmp->icmp_discon_pending = 1;
738*0Sstevel@tonic-gate 
739*0Sstevel@tonic-gate 	/* Append the T_OK_ACK to the T_BIND_REQ for icmp_rput */
740*0Sstevel@tonic-gate 	linkb(mp1, mp);
741*0Sstevel@tonic-gate 	putnext(q, mp1);
742*0Sstevel@tonic-gate }
743*0Sstevel@tonic-gate 
744*0Sstevel@tonic-gate /* This routine creates a T_ERROR_ACK message and passes it upstream. */
745*0Sstevel@tonic-gate static void
746*0Sstevel@tonic-gate icmp_err_ack(queue_t *q, mblk_t *mp, t_scalar_t t_error, int sys_error)
747*0Sstevel@tonic-gate {
748*0Sstevel@tonic-gate 	if ((mp = mi_tpi_err_ack_alloc(mp, t_error, sys_error)) != NULL)
749*0Sstevel@tonic-gate 		qreply(q, mp);
750*0Sstevel@tonic-gate }
751*0Sstevel@tonic-gate 
752*0Sstevel@tonic-gate /* Shorthand to generate and send TPI error acks to our client */
753*0Sstevel@tonic-gate static void
754*0Sstevel@tonic-gate icmp_err_ack_prim(queue_t *q, mblk_t *mp, t_scalar_t primitive,
755*0Sstevel@tonic-gate     t_scalar_t t_error, int sys_error)
756*0Sstevel@tonic-gate {
757*0Sstevel@tonic-gate 	struct T_error_ack	*teackp;
758*0Sstevel@tonic-gate 
759*0Sstevel@tonic-gate 	if ((mp = tpi_ack_alloc(mp, sizeof (struct T_error_ack),
760*0Sstevel@tonic-gate 	    M_PCPROTO, T_ERROR_ACK)) != NULL) {
761*0Sstevel@tonic-gate 		teackp = (struct T_error_ack *)mp->b_rptr;
762*0Sstevel@tonic-gate 		teackp->ERROR_prim = primitive;
763*0Sstevel@tonic-gate 		teackp->TLI_error = t_error;
764*0Sstevel@tonic-gate 		teackp->UNIX_error = sys_error;
765*0Sstevel@tonic-gate 		qreply(q, mp);
766*0Sstevel@tonic-gate 	}
767*0Sstevel@tonic-gate }
768*0Sstevel@tonic-gate 
769*0Sstevel@tonic-gate /*
770*0Sstevel@tonic-gate  * icmp_icmp_error is called by icmp_rput to process ICMP
771*0Sstevel@tonic-gate  * messages passed up by IP.
772*0Sstevel@tonic-gate  * Generates the appropriate T_UDERROR_IND for permanent
773*0Sstevel@tonic-gate  * (non-transient) errors.
774*0Sstevel@tonic-gate  * Assumes that IP has pulled up everything up to and including
775*0Sstevel@tonic-gate  * the ICMP header.
776*0Sstevel@tonic-gate  */
777*0Sstevel@tonic-gate static void
778*0Sstevel@tonic-gate icmp_icmp_error(queue_t *q, mblk_t *mp)
779*0Sstevel@tonic-gate {
780*0Sstevel@tonic-gate 	icmph_t *icmph;
781*0Sstevel@tonic-gate 	ipha_t	*ipha;
782*0Sstevel@tonic-gate 	int	iph_hdr_length;
783*0Sstevel@tonic-gate 	sin_t	sin;
784*0Sstevel@tonic-gate 	sin6_t	sin6;
785*0Sstevel@tonic-gate 	mblk_t	*mp1;
786*0Sstevel@tonic-gate 	int	error = 0;
787*0Sstevel@tonic-gate 	icmp_t	*icmp = (icmp_t *)q->q_ptr;
788*0Sstevel@tonic-gate 
789*0Sstevel@tonic-gate 	/*
790*0Sstevel@tonic-gate 	 * Deliver T_UDERROR_IND when the application has asked for it.
791*0Sstevel@tonic-gate 	 * The socket layer enables this automatically when connected.
792*0Sstevel@tonic-gate 	 */
793*0Sstevel@tonic-gate 	if (!icmp->icmp_dgram_errind) {
794*0Sstevel@tonic-gate 		freemsg(mp);
795*0Sstevel@tonic-gate 		return;
796*0Sstevel@tonic-gate 	}
797*0Sstevel@tonic-gate 
798*0Sstevel@tonic-gate 	ipha = (ipha_t *)mp->b_rptr;
799*0Sstevel@tonic-gate 
800*0Sstevel@tonic-gate 	if (IPH_HDR_VERSION(ipha) != IPV4_VERSION) {
801*0Sstevel@tonic-gate 		ASSERT(IPH_HDR_VERSION(ipha) == IPV6_VERSION);
802*0Sstevel@tonic-gate 		icmp_icmp_error_ipv6(q, mp);
803*0Sstevel@tonic-gate 		return;
804*0Sstevel@tonic-gate 	}
805*0Sstevel@tonic-gate 	ASSERT(IPH_HDR_VERSION(ipha) == IPV4_VERSION);
806*0Sstevel@tonic-gate 
807*0Sstevel@tonic-gate 	iph_hdr_length = IPH_HDR_LENGTH(ipha);
808*0Sstevel@tonic-gate 	icmph = (icmph_t *)(&mp->b_rptr[iph_hdr_length]);
809*0Sstevel@tonic-gate 	ipha = (ipha_t *)&icmph[1];
810*0Sstevel@tonic-gate 	iph_hdr_length = IPH_HDR_LENGTH(ipha);
811*0Sstevel@tonic-gate 
812*0Sstevel@tonic-gate 	switch (icmph->icmph_type) {
813*0Sstevel@tonic-gate 	case ICMP_DEST_UNREACHABLE:
814*0Sstevel@tonic-gate 		switch (icmph->icmph_code) {
815*0Sstevel@tonic-gate 		case ICMP_FRAGMENTATION_NEEDED:
816*0Sstevel@tonic-gate 			/*
817*0Sstevel@tonic-gate 			 * IP has already adjusted the path MTU.
818*0Sstevel@tonic-gate 			 * XXX Somehow pass MTU indication to application?
819*0Sstevel@tonic-gate 			 */
820*0Sstevel@tonic-gate 			break;
821*0Sstevel@tonic-gate 		case ICMP_PORT_UNREACHABLE:
822*0Sstevel@tonic-gate 		case ICMP_PROTOCOL_UNREACHABLE:
823*0Sstevel@tonic-gate 			error = ECONNREFUSED;
824*0Sstevel@tonic-gate 			break;
825*0Sstevel@tonic-gate 		default:
826*0Sstevel@tonic-gate 			/* Transient errors */
827*0Sstevel@tonic-gate 			break;
828*0Sstevel@tonic-gate 		}
829*0Sstevel@tonic-gate 		break;
830*0Sstevel@tonic-gate 	default:
831*0Sstevel@tonic-gate 		/* Transient errors */
832*0Sstevel@tonic-gate 		break;
833*0Sstevel@tonic-gate 	}
834*0Sstevel@tonic-gate 	if (error == 0) {
835*0Sstevel@tonic-gate 		freemsg(mp);
836*0Sstevel@tonic-gate 		return;
837*0Sstevel@tonic-gate 	}
838*0Sstevel@tonic-gate 
839*0Sstevel@tonic-gate 	switch (icmp->icmp_family) {
840*0Sstevel@tonic-gate 	case AF_INET:
841*0Sstevel@tonic-gate 		sin = sin_null;
842*0Sstevel@tonic-gate 		sin.sin_family = AF_INET;
843*0Sstevel@tonic-gate 		sin.sin_addr.s_addr = ipha->ipha_dst;
844*0Sstevel@tonic-gate 		mp1 = mi_tpi_uderror_ind((char *)&sin, sizeof (sin_t), NULL, 0,
845*0Sstevel@tonic-gate 		    error);
846*0Sstevel@tonic-gate 		break;
847*0Sstevel@tonic-gate 	case AF_INET6:
848*0Sstevel@tonic-gate 		sin6 = sin6_null;
849*0Sstevel@tonic-gate 		sin6.sin6_family = AF_INET6;
850*0Sstevel@tonic-gate 		IN6_IPADDR_TO_V4MAPPED(ipha->ipha_dst, &sin6.sin6_addr);
851*0Sstevel@tonic-gate 
852*0Sstevel@tonic-gate 		mp1 = mi_tpi_uderror_ind((char *)&sin6, sizeof (sin6_t),
853*0Sstevel@tonic-gate 		    NULL, 0, error);
854*0Sstevel@tonic-gate 		break;
855*0Sstevel@tonic-gate 	}
856*0Sstevel@tonic-gate 	if (mp1)
857*0Sstevel@tonic-gate 		putnext(q, mp1);
858*0Sstevel@tonic-gate 	freemsg(mp);
859*0Sstevel@tonic-gate }
860*0Sstevel@tonic-gate 
861*0Sstevel@tonic-gate /*
862*0Sstevel@tonic-gate  * icmp_icmp_error_ipv6 is called by icmp_icmp_error to process ICMPv6
863*0Sstevel@tonic-gate  * for IPv6 packets.
864*0Sstevel@tonic-gate  * Send permanent (non-transient) errors upstream.
865*0Sstevel@tonic-gate  * Assumes that IP has pulled up all the extension headers as well
866*0Sstevel@tonic-gate  * as the ICMPv6 header.
867*0Sstevel@tonic-gate  */
868*0Sstevel@tonic-gate static void
869*0Sstevel@tonic-gate icmp_icmp_error_ipv6(queue_t *q, mblk_t *mp)
870*0Sstevel@tonic-gate {
871*0Sstevel@tonic-gate 	icmp6_t		*icmp6;
872*0Sstevel@tonic-gate 	ip6_t		*ip6h, *outer_ip6h;
873*0Sstevel@tonic-gate 	uint16_t	iph_hdr_length;
874*0Sstevel@tonic-gate 	uint8_t		*nexthdrp;
875*0Sstevel@tonic-gate 	sin6_t		sin6;
876*0Sstevel@tonic-gate 	mblk_t		*mp1;
877*0Sstevel@tonic-gate 	int		error = 0;
878*0Sstevel@tonic-gate 	icmp_t		*icmp = (icmp_t *)q->q_ptr;
879*0Sstevel@tonic-gate 
880*0Sstevel@tonic-gate 	outer_ip6h = (ip6_t *)mp->b_rptr;
881*0Sstevel@tonic-gate 	if (outer_ip6h->ip6_nxt != IPPROTO_ICMPV6)
882*0Sstevel@tonic-gate 		iph_hdr_length = ip_hdr_length_v6(mp, outer_ip6h);
883*0Sstevel@tonic-gate 	else
884*0Sstevel@tonic-gate 		iph_hdr_length = IPV6_HDR_LEN;
885*0Sstevel@tonic-gate 
886*0Sstevel@tonic-gate 	icmp6 = (icmp6_t *)&mp->b_rptr[iph_hdr_length];
887*0Sstevel@tonic-gate 	ip6h = (ip6_t *)&icmp6[1];
888*0Sstevel@tonic-gate 	if (!ip_hdr_length_nexthdr_v6(mp, ip6h, &iph_hdr_length, &nexthdrp)) {
889*0Sstevel@tonic-gate 		freemsg(mp);
890*0Sstevel@tonic-gate 		return;
891*0Sstevel@tonic-gate 	}
892*0Sstevel@tonic-gate 	if (*nexthdrp != icmp->icmp_proto) {
893*0Sstevel@tonic-gate 		/*
894*0Sstevel@tonic-gate 		 * Could have switched icmp_proto after while ip did fanout of
895*0Sstevel@tonic-gate 		 * this message
896*0Sstevel@tonic-gate 		 */
897*0Sstevel@tonic-gate 		freemsg(mp);
898*0Sstevel@tonic-gate 		return;
899*0Sstevel@tonic-gate 	}
900*0Sstevel@tonic-gate 	switch (icmp6->icmp6_type) {
901*0Sstevel@tonic-gate 	case ICMP6_DST_UNREACH:
902*0Sstevel@tonic-gate 		switch (icmp6->icmp6_code) {
903*0Sstevel@tonic-gate 		case ICMP6_DST_UNREACH_NOPORT:
904*0Sstevel@tonic-gate 			error = ECONNREFUSED;
905*0Sstevel@tonic-gate 			break;
906*0Sstevel@tonic-gate 		case ICMP6_DST_UNREACH_ADMIN:
907*0Sstevel@tonic-gate 		case ICMP6_DST_UNREACH_NOROUTE:
908*0Sstevel@tonic-gate 		case ICMP6_DST_UNREACH_BEYONDSCOPE:
909*0Sstevel@tonic-gate 		case ICMP6_DST_UNREACH_ADDR:
910*0Sstevel@tonic-gate 			/* Transient errors */
911*0Sstevel@tonic-gate 			break;
912*0Sstevel@tonic-gate 		default:
913*0Sstevel@tonic-gate 			break;
914*0Sstevel@tonic-gate 		}
915*0Sstevel@tonic-gate 		break;
916*0Sstevel@tonic-gate 	case ICMP6_PACKET_TOO_BIG: {
917*0Sstevel@tonic-gate 		struct T_unitdata_ind	*tudi;
918*0Sstevel@tonic-gate 		struct T_opthdr		*toh;
919*0Sstevel@tonic-gate 		size_t			udi_size;
920*0Sstevel@tonic-gate 		mblk_t			*newmp;
921*0Sstevel@tonic-gate 		t_scalar_t		opt_length = sizeof (struct T_opthdr) +
922*0Sstevel@tonic-gate 		    sizeof (struct ip6_mtuinfo);
923*0Sstevel@tonic-gate 		sin6_t			*sin6;
924*0Sstevel@tonic-gate 		struct ip6_mtuinfo	*mtuinfo;
925*0Sstevel@tonic-gate 
926*0Sstevel@tonic-gate 		/*
927*0Sstevel@tonic-gate 		 * If the application has requested to receive path mtu
928*0Sstevel@tonic-gate 		 * information, send up an empty message containing an
929*0Sstevel@tonic-gate 		 * IPV6_PATHMTU ancillary data item.
930*0Sstevel@tonic-gate 		 */
931*0Sstevel@tonic-gate 		if (!icmp->icmp_ipv6_recvpathmtu)
932*0Sstevel@tonic-gate 			break;
933*0Sstevel@tonic-gate 
934*0Sstevel@tonic-gate 		udi_size = sizeof (struct T_unitdata_ind) + sizeof (sin6_t) +
935*0Sstevel@tonic-gate 		    opt_length;
936*0Sstevel@tonic-gate 		if ((newmp = allocb(udi_size, BPRI_MED)) == NULL) {
937*0Sstevel@tonic-gate 			BUMP_MIB(&rawip_mib, rawipInErrors);
938*0Sstevel@tonic-gate 			break;
939*0Sstevel@tonic-gate 		}
940*0Sstevel@tonic-gate 
941*0Sstevel@tonic-gate 		/*
942*0Sstevel@tonic-gate 		 * newmp->b_cont is left to NULL on purpose.  This is an
943*0Sstevel@tonic-gate 		 * empty message containing only ancillary data.
944*0Sstevel@tonic-gate 		 */
945*0Sstevel@tonic-gate 		newmp->b_datap->db_type = M_PROTO;
946*0Sstevel@tonic-gate 		tudi = (struct T_unitdata_ind *)newmp->b_rptr;
947*0Sstevel@tonic-gate 		newmp->b_wptr = (uchar_t *)tudi + udi_size;
948*0Sstevel@tonic-gate 		tudi->PRIM_type = T_UNITDATA_IND;
949*0Sstevel@tonic-gate 		tudi->SRC_length = sizeof (sin6_t);
950*0Sstevel@tonic-gate 		tudi->SRC_offset = sizeof (struct T_unitdata_ind);
951*0Sstevel@tonic-gate 		tudi->OPT_offset = tudi->SRC_offset + sizeof (sin6_t);
952*0Sstevel@tonic-gate 		tudi->OPT_length = opt_length;
953*0Sstevel@tonic-gate 
954*0Sstevel@tonic-gate 		sin6 = (sin6_t *)&tudi[1];
955*0Sstevel@tonic-gate 		bzero(sin6, sizeof (sin6_t));
956*0Sstevel@tonic-gate 		sin6->sin6_family = AF_INET6;
957*0Sstevel@tonic-gate 		sin6->sin6_addr = icmp->icmp_v6dst;
958*0Sstevel@tonic-gate 
959*0Sstevel@tonic-gate 		toh = (struct T_opthdr *)&sin6[1];
960*0Sstevel@tonic-gate 		toh->level = IPPROTO_IPV6;
961*0Sstevel@tonic-gate 		toh->name = IPV6_PATHMTU;
962*0Sstevel@tonic-gate 		toh->len = opt_length;
963*0Sstevel@tonic-gate 		toh->status = 0;
964*0Sstevel@tonic-gate 
965*0Sstevel@tonic-gate 		mtuinfo = (struct ip6_mtuinfo *)&toh[1];
966*0Sstevel@tonic-gate 		bzero(mtuinfo, sizeof (struct ip6_mtuinfo));
967*0Sstevel@tonic-gate 		mtuinfo->ip6m_addr.sin6_family = AF_INET6;
968*0Sstevel@tonic-gate 		mtuinfo->ip6m_addr.sin6_addr = ip6h->ip6_dst;
969*0Sstevel@tonic-gate 		mtuinfo->ip6m_mtu = icmp6->icmp6_mtu;
970*0Sstevel@tonic-gate 		/*
971*0Sstevel@tonic-gate 		 * We've consumed everything we need from the original
972*0Sstevel@tonic-gate 		 * message.  Free it, then send our empty message.
973*0Sstevel@tonic-gate 		 */
974*0Sstevel@tonic-gate 		freemsg(mp);
975*0Sstevel@tonic-gate 		putnext(q, newmp);
976*0Sstevel@tonic-gate 		return;
977*0Sstevel@tonic-gate 	}
978*0Sstevel@tonic-gate 	case ICMP6_TIME_EXCEEDED:
979*0Sstevel@tonic-gate 		/* Transient errors */
980*0Sstevel@tonic-gate 		break;
981*0Sstevel@tonic-gate 	case ICMP6_PARAM_PROB:
982*0Sstevel@tonic-gate 		/* If this corresponds to an ICMP_PROTOCOL_UNREACHABLE */
983*0Sstevel@tonic-gate 		if (icmp6->icmp6_code == ICMP6_PARAMPROB_NEXTHEADER &&
984*0Sstevel@tonic-gate 		    (uchar_t *)ip6h + icmp6->icmp6_pptr ==
985*0Sstevel@tonic-gate 		    (uchar_t *)nexthdrp) {
986*0Sstevel@tonic-gate 			error = ECONNREFUSED;
987*0Sstevel@tonic-gate 			break;
988*0Sstevel@tonic-gate 		}
989*0Sstevel@tonic-gate 		break;
990*0Sstevel@tonic-gate 	}
991*0Sstevel@tonic-gate 	if (error == 0) {
992*0Sstevel@tonic-gate 		freemsg(mp);
993*0Sstevel@tonic-gate 		return;
994*0Sstevel@tonic-gate 	}
995*0Sstevel@tonic-gate 
996*0Sstevel@tonic-gate 	sin6 = sin6_null;
997*0Sstevel@tonic-gate 	sin6.sin6_family = AF_INET6;
998*0Sstevel@tonic-gate 	sin6.sin6_addr = ip6h->ip6_dst;
999*0Sstevel@tonic-gate 	sin6.sin6_flowinfo = ip6h->ip6_vcf & ~IPV6_VERS_AND_FLOW_MASK;
1000*0Sstevel@tonic-gate 
1001*0Sstevel@tonic-gate 	mp1 = mi_tpi_uderror_ind((char *)&sin6, sizeof (sin6_t), NULL, 0,
1002*0Sstevel@tonic-gate 	    error);
1003*0Sstevel@tonic-gate 	if (mp1)
1004*0Sstevel@tonic-gate 		putnext(q, mp1);
1005*0Sstevel@tonic-gate 	freemsg(mp);
1006*0Sstevel@tonic-gate }
1007*0Sstevel@tonic-gate 
1008*0Sstevel@tonic-gate /*
1009*0Sstevel@tonic-gate  * This routine responds to T_ADDR_REQ messages.  It is called by icmp_wput.
1010*0Sstevel@tonic-gate  * The local address is filled in if endpoint is bound. The remote address
1011*0Sstevel@tonic-gate  * is filled in if remote address has been precified ("connected endpoint")
1012*0Sstevel@tonic-gate  * (The concept of connected CLTS sockets is alien to published TPI
1013*0Sstevel@tonic-gate  *  but we support it anyway).
1014*0Sstevel@tonic-gate  */
1015*0Sstevel@tonic-gate static void
1016*0Sstevel@tonic-gate icmp_addr_req(queue_t *q, mblk_t *mp)
1017*0Sstevel@tonic-gate {
1018*0Sstevel@tonic-gate 	icmp_t	*icmp = (icmp_t *)q->q_ptr;
1019*0Sstevel@tonic-gate 	mblk_t	*ackmp;
1020*0Sstevel@tonic-gate 	struct T_addr_ack *taa;
1021*0Sstevel@tonic-gate 
1022*0Sstevel@tonic-gate 	/* Make it large enough for worst case */
1023*0Sstevel@tonic-gate 	ackmp = reallocb(mp, sizeof (struct T_addr_ack) +
1024*0Sstevel@tonic-gate 	    2 * sizeof (sin6_t), 1);
1025*0Sstevel@tonic-gate 	if (ackmp == NULL) {
1026*0Sstevel@tonic-gate 		icmp_err_ack(q, mp, TSYSERR, ENOMEM);
1027*0Sstevel@tonic-gate 		return;
1028*0Sstevel@tonic-gate 	}
1029*0Sstevel@tonic-gate 	taa = (struct T_addr_ack *)ackmp->b_rptr;
1030*0Sstevel@tonic-gate 
1031*0Sstevel@tonic-gate 	bzero(taa, sizeof (struct T_addr_ack));
1032*0Sstevel@tonic-gate 	ackmp->b_wptr = (uchar_t *)&taa[1];
1033*0Sstevel@tonic-gate 
1034*0Sstevel@tonic-gate 	taa->PRIM_type = T_ADDR_ACK;
1035*0Sstevel@tonic-gate 	ackmp->b_datap->db_type = M_PCPROTO;
1036*0Sstevel@tonic-gate 
1037*0Sstevel@tonic-gate 	/*
1038*0Sstevel@tonic-gate 	 * Note: Following code assumes 32 bit alignment of basic
1039*0Sstevel@tonic-gate 	 * data structures like sin_t and struct T_addr_ack.
1040*0Sstevel@tonic-gate 	 */
1041*0Sstevel@tonic-gate 	if (icmp->icmp_state != TS_UNBND) {
1042*0Sstevel@tonic-gate 		/*
1043*0Sstevel@tonic-gate 		 * Fill in local address
1044*0Sstevel@tonic-gate 		 */
1045*0Sstevel@tonic-gate 		taa->LOCADDR_offset = sizeof (*taa);
1046*0Sstevel@tonic-gate 		if (icmp->icmp_family == AF_INET) {
1047*0Sstevel@tonic-gate 			sin_t	*sin;
1048*0Sstevel@tonic-gate 
1049*0Sstevel@tonic-gate 			taa->LOCADDR_length = sizeof (sin_t);
1050*0Sstevel@tonic-gate 			sin = (sin_t *)&taa[1];
1051*0Sstevel@tonic-gate 			/* Fill zeroes and then intialize non-zero fields */
1052*0Sstevel@tonic-gate 			*sin = sin_null;
1053*0Sstevel@tonic-gate 			sin->sin_family = AF_INET;
1054*0Sstevel@tonic-gate 			if (!IN6_IS_ADDR_V4MAPPED_ANY(&icmp->icmp_v6src) &&
1055*0Sstevel@tonic-gate 			    !IN6_IS_ADDR_UNSPECIFIED(&icmp->icmp_v6src)) {
1056*0Sstevel@tonic-gate 				IN6_V4MAPPED_TO_IPADDR(&icmp->icmp_v6src,
1057*0Sstevel@tonic-gate 				    sin->sin_addr.s_addr);
1058*0Sstevel@tonic-gate 			} else {
1059*0Sstevel@tonic-gate 				/*
1060*0Sstevel@tonic-gate 				 * INADDR_ANY
1061*0Sstevel@tonic-gate 				 * icmp_v6src is not set, we might be bound to
1062*0Sstevel@tonic-gate 				 * broadcast/multicast. Use icmp_bound_v6src as
1063*0Sstevel@tonic-gate 				 * local address instead (that could
1064*0Sstevel@tonic-gate 				 * also still be INADDR_ANY)
1065*0Sstevel@tonic-gate 				 */
1066*0Sstevel@tonic-gate 				IN6_V4MAPPED_TO_IPADDR(&icmp->icmp_bound_v6src,
1067*0Sstevel@tonic-gate 				    sin->sin_addr.s_addr);
1068*0Sstevel@tonic-gate 			}
1069*0Sstevel@tonic-gate 			ackmp->b_wptr = (uchar_t *)&sin[1];
1070*0Sstevel@tonic-gate 		} else {
1071*0Sstevel@tonic-gate 			sin6_t	*sin6;
1072*0Sstevel@tonic-gate 
1073*0Sstevel@tonic-gate 			ASSERT(icmp->icmp_family == AF_INET6);
1074*0Sstevel@tonic-gate 			taa->LOCADDR_length = sizeof (sin6_t);
1075*0Sstevel@tonic-gate 			sin6 = (sin6_t *)&taa[1];
1076*0Sstevel@tonic-gate 			/* Fill zeroes and then intialize non-zero fields */
1077*0Sstevel@tonic-gate 			*sin6 = sin6_null;
1078*0Sstevel@tonic-gate 			sin6->sin6_family = AF_INET6;
1079*0Sstevel@tonic-gate 			if (!IN6_IS_ADDR_UNSPECIFIED(&icmp->icmp_v6src)) {
1080*0Sstevel@tonic-gate 				sin6->sin6_addr = icmp->icmp_v6src;
1081*0Sstevel@tonic-gate 			} else {
1082*0Sstevel@tonic-gate 				/*
1083*0Sstevel@tonic-gate 				 * UNSPECIFIED
1084*0Sstevel@tonic-gate 				 * icmp_v6src is not set, we might be bound to
1085*0Sstevel@tonic-gate 				 * broadcast/multicast. Use icmp_bound_v6src as
1086*0Sstevel@tonic-gate 				 * local address instead (that could
1087*0Sstevel@tonic-gate 				 * also still be UNSPECIFIED)
1088*0Sstevel@tonic-gate 				 */
1089*0Sstevel@tonic-gate 				sin6->sin6_addr = icmp->icmp_bound_v6src;
1090*0Sstevel@tonic-gate 			}
1091*0Sstevel@tonic-gate 			ackmp->b_wptr = (uchar_t *)&sin6[1];
1092*0Sstevel@tonic-gate 		}
1093*0Sstevel@tonic-gate 	}
1094*0Sstevel@tonic-gate 	ASSERT(ackmp->b_wptr <= ackmp->b_datap->db_lim);
1095*0Sstevel@tonic-gate 	qreply(q, ackmp);
1096*0Sstevel@tonic-gate }
1097*0Sstevel@tonic-gate 
1098*0Sstevel@tonic-gate static void
1099*0Sstevel@tonic-gate icmp_copy_info(struct T_info_ack *tap, icmp_t *icmp)
1100*0Sstevel@tonic-gate {
1101*0Sstevel@tonic-gate 	*tap = icmp_g_t_info_ack;
1102*0Sstevel@tonic-gate 
1103*0Sstevel@tonic-gate 	if (icmp->icmp_family == AF_INET6)
1104*0Sstevel@tonic-gate 		tap->ADDR_size = sizeof (sin6_t);
1105*0Sstevel@tonic-gate 	else
1106*0Sstevel@tonic-gate 		tap->ADDR_size = sizeof (sin_t);
1107*0Sstevel@tonic-gate 	tap->CURRENT_state = icmp->icmp_state;
1108*0Sstevel@tonic-gate 	tap->OPT_size = icmp_max_optsize;
1109*0Sstevel@tonic-gate }
1110*0Sstevel@tonic-gate 
1111*0Sstevel@tonic-gate /*
1112*0Sstevel@tonic-gate  * This routine responds to T_CAPABILITY_REQ messages.  It is called by
1113*0Sstevel@tonic-gate  * icmp_wput.  Much of the T_CAPABILITY_ACK information is copied from
1114*0Sstevel@tonic-gate  * icmp_g_t_info_ack.  The current state of the stream is copied from
1115*0Sstevel@tonic-gate  * icmp_state.
1116*0Sstevel@tonic-gate  */
1117*0Sstevel@tonic-gate static void
1118*0Sstevel@tonic-gate icmp_capability_req(queue_t *q, mblk_t *mp)
1119*0Sstevel@tonic-gate {
1120*0Sstevel@tonic-gate 	icmp_t			*icmp = (icmp_t *)q->q_ptr;
1121*0Sstevel@tonic-gate 	t_uscalar_t		cap_bits1;
1122*0Sstevel@tonic-gate 	struct T_capability_ack	*tcap;
1123*0Sstevel@tonic-gate 
1124*0Sstevel@tonic-gate 	cap_bits1 = ((struct T_capability_req *)mp->b_rptr)->CAP_bits1;
1125*0Sstevel@tonic-gate 
1126*0Sstevel@tonic-gate 	mp = tpi_ack_alloc(mp, sizeof (struct T_capability_ack),
1127*0Sstevel@tonic-gate 		mp->b_datap->db_type, T_CAPABILITY_ACK);
1128*0Sstevel@tonic-gate 	if (!mp)
1129*0Sstevel@tonic-gate 		return;
1130*0Sstevel@tonic-gate 
1131*0Sstevel@tonic-gate 	tcap = (struct T_capability_ack *)mp->b_rptr;
1132*0Sstevel@tonic-gate 	tcap->CAP_bits1 = 0;
1133*0Sstevel@tonic-gate 
1134*0Sstevel@tonic-gate 	if (cap_bits1 & TC1_INFO) {
1135*0Sstevel@tonic-gate 		icmp_copy_info(&tcap->INFO_ack, icmp);
1136*0Sstevel@tonic-gate 		tcap->CAP_bits1 |= TC1_INFO;
1137*0Sstevel@tonic-gate 	}
1138*0Sstevel@tonic-gate 
1139*0Sstevel@tonic-gate 	qreply(q, mp);
1140*0Sstevel@tonic-gate }
1141*0Sstevel@tonic-gate 
1142*0Sstevel@tonic-gate /*
1143*0Sstevel@tonic-gate  * This routine responds to T_INFO_REQ messages.  It is called by icmp_wput.
1144*0Sstevel@tonic-gate  * Most of the T_INFO_ACK information is copied from icmp_g_t_info_ack.
1145*0Sstevel@tonic-gate  * The current state of the stream is copied from icmp_state.
1146*0Sstevel@tonic-gate  */
1147*0Sstevel@tonic-gate static void
1148*0Sstevel@tonic-gate icmp_info_req(queue_t *q, mblk_t *mp)
1149*0Sstevel@tonic-gate {
1150*0Sstevel@tonic-gate 	icmp_t	*icmp = (icmp_t *)q->q_ptr;
1151*0Sstevel@tonic-gate 
1152*0Sstevel@tonic-gate 	mp = tpi_ack_alloc(mp, sizeof (struct T_info_ack), M_PCPROTO,
1153*0Sstevel@tonic-gate 	    T_INFO_ACK);
1154*0Sstevel@tonic-gate 	if (!mp)
1155*0Sstevel@tonic-gate 		return;
1156*0Sstevel@tonic-gate 	icmp_copy_info((struct T_info_ack *)mp->b_rptr, icmp);
1157*0Sstevel@tonic-gate 	qreply(q, mp);
1158*0Sstevel@tonic-gate }
1159*0Sstevel@tonic-gate 
1160*0Sstevel@tonic-gate /*
1161*0Sstevel@tonic-gate  * IP recognizes seven kinds of bind requests:
1162*0Sstevel@tonic-gate  *
1163*0Sstevel@tonic-gate  * - A zero-length address binds only to the protocol number.
1164*0Sstevel@tonic-gate  *
1165*0Sstevel@tonic-gate  * - A 4-byte address is treated as a request to
1166*0Sstevel@tonic-gate  * validate that the address is a valid local IPv4
1167*0Sstevel@tonic-gate  * address, appropriate for an application to bind to.
1168*0Sstevel@tonic-gate  * IP does the verification, but does not make any note
1169*0Sstevel@tonic-gate  * of the address at this time.
1170*0Sstevel@tonic-gate  *
1171*0Sstevel@tonic-gate  * - A 16-byte address contains is treated as a request
1172*0Sstevel@tonic-gate  * to validate a local IPv6 address, as the 4-byte
1173*0Sstevel@tonic-gate  * address case above.
1174*0Sstevel@tonic-gate  *
1175*0Sstevel@tonic-gate  * - A 16-byte sockaddr_in to validate the local IPv4 address and also
1176*0Sstevel@tonic-gate  * use it for the inbound fanout of packets.
1177*0Sstevel@tonic-gate  *
1178*0Sstevel@tonic-gate  * - A 24-byte sockaddr_in6 to validate the local IPv6 address and also
1179*0Sstevel@tonic-gate  * use it for the inbound fanout of packets.
1180*0Sstevel@tonic-gate  *
1181*0Sstevel@tonic-gate  * - A 12-byte address (ipa_conn_t) containing complete IPv4 fanout
1182*0Sstevel@tonic-gate  * information consisting of local and remote addresses
1183*0Sstevel@tonic-gate  * and ports (unused for raw sockets).  In this case, the addresses are both
1184*0Sstevel@tonic-gate  * validated as appropriate for this operation, and, if
1185*0Sstevel@tonic-gate  * so, the information is retained for use in the
1186*0Sstevel@tonic-gate  * inbound fanout.
1187*0Sstevel@tonic-gate  *
1188*0Sstevel@tonic-gate  * - A 36-byte address address (ipa6_conn_t) containing complete IPv6
1189*0Sstevel@tonic-gate  * fanout information, like the 12-byte case above.
1190*0Sstevel@tonic-gate  *
1191*0Sstevel@tonic-gate  * IP will also fill in the IRE request mblk with information
1192*0Sstevel@tonic-gate  * regarding our peer.  In all cases, we notify IP of our protocol
1193*0Sstevel@tonic-gate  * type by appending a single protocol byte to the bind request.
1194*0Sstevel@tonic-gate  */
1195*0Sstevel@tonic-gate static mblk_t *
1196*0Sstevel@tonic-gate icmp_ip_bind_mp(icmp_t *icmp, t_scalar_t bind_prim, t_scalar_t addr_length,
1197*0Sstevel@tonic-gate     in_port_t fport)
1198*0Sstevel@tonic-gate {
1199*0Sstevel@tonic-gate 	char	*cp;
1200*0Sstevel@tonic-gate 	mblk_t	*mp;
1201*0Sstevel@tonic-gate 	struct T_bind_req *tbr;
1202*0Sstevel@tonic-gate 	ipa_conn_t	*ac;
1203*0Sstevel@tonic-gate 	ipa6_conn_t	*ac6;
1204*0Sstevel@tonic-gate 	sin_t		*sin;
1205*0Sstevel@tonic-gate 	sin6_t		*sin6;
1206*0Sstevel@tonic-gate 
1207*0Sstevel@tonic-gate 	ASSERT(bind_prim == O_T_BIND_REQ || bind_prim == T_BIND_REQ);
1208*0Sstevel@tonic-gate 
1209*0Sstevel@tonic-gate 	mp = allocb(sizeof (*tbr) + addr_length + 1, BPRI_HI);
1210*0Sstevel@tonic-gate 	if (mp == NULL)
1211*0Sstevel@tonic-gate 		return (NULL);
1212*0Sstevel@tonic-gate 	mp->b_datap->db_type = M_PROTO;
1213*0Sstevel@tonic-gate 	tbr = (struct T_bind_req *)mp->b_rptr;
1214*0Sstevel@tonic-gate 	tbr->PRIM_type = bind_prim;
1215*0Sstevel@tonic-gate 	tbr->ADDR_offset = sizeof (*tbr);
1216*0Sstevel@tonic-gate 	tbr->CONIND_number = 0;
1217*0Sstevel@tonic-gate 	tbr->ADDR_length = addr_length;
1218*0Sstevel@tonic-gate 	cp = (char *)&tbr[1];
1219*0Sstevel@tonic-gate 	switch (addr_length) {
1220*0Sstevel@tonic-gate 	case sizeof (ipa_conn_t):
1221*0Sstevel@tonic-gate 		ASSERT(icmp->icmp_family == AF_INET);
1222*0Sstevel@tonic-gate 		/* Append a request for an IRE */
1223*0Sstevel@tonic-gate 		mp->b_cont = allocb(sizeof (ire_t), BPRI_HI);
1224*0Sstevel@tonic-gate 		if (mp->b_cont == NULL) {
1225*0Sstevel@tonic-gate 			freemsg(mp);
1226*0Sstevel@tonic-gate 			return (NULL);
1227*0Sstevel@tonic-gate 		}
1228*0Sstevel@tonic-gate 		mp->b_cont->b_wptr += sizeof (ire_t);
1229*0Sstevel@tonic-gate 		mp->b_cont->b_datap->db_type = IRE_DB_REQ_TYPE;
1230*0Sstevel@tonic-gate 
1231*0Sstevel@tonic-gate 		/* cp known to be 32 bit aligned */
1232*0Sstevel@tonic-gate 		ac = (ipa_conn_t *)cp;
1233*0Sstevel@tonic-gate 		ac->ac_laddr = V4_PART_OF_V6(icmp->icmp_v6src);
1234*0Sstevel@tonic-gate 		ac->ac_faddr = V4_PART_OF_V6(icmp->icmp_v6dst);
1235*0Sstevel@tonic-gate 		ac->ac_fport = fport;
1236*0Sstevel@tonic-gate 		ac->ac_lport = 0;
1237*0Sstevel@tonic-gate 		break;
1238*0Sstevel@tonic-gate 
1239*0Sstevel@tonic-gate 	case sizeof (ipa6_conn_t):
1240*0Sstevel@tonic-gate 		ASSERT(icmp->icmp_family == AF_INET6);
1241*0Sstevel@tonic-gate 		/* Append a request for an IRE */
1242*0Sstevel@tonic-gate 		mp->b_cont = allocb(sizeof (ire_t), BPRI_HI);
1243*0Sstevel@tonic-gate 		if (mp->b_cont == NULL) {
1244*0Sstevel@tonic-gate 			freemsg(mp);
1245*0Sstevel@tonic-gate 			return (NULL);
1246*0Sstevel@tonic-gate 		}
1247*0Sstevel@tonic-gate 		mp->b_cont->b_wptr += sizeof (ire_t);
1248*0Sstevel@tonic-gate 		mp->b_cont->b_datap->db_type = IRE_DB_REQ_TYPE;
1249*0Sstevel@tonic-gate 
1250*0Sstevel@tonic-gate 		/* cp known to be 32 bit aligned */
1251*0Sstevel@tonic-gate 		ac6 = (ipa6_conn_t *)cp;
1252*0Sstevel@tonic-gate 		ac6->ac6_laddr = icmp->icmp_v6src;
1253*0Sstevel@tonic-gate 		ac6->ac6_faddr = icmp->icmp_v6dst;
1254*0Sstevel@tonic-gate 		ac6->ac6_fport = fport;
1255*0Sstevel@tonic-gate 		ac6->ac6_lport = 0;
1256*0Sstevel@tonic-gate 		break;
1257*0Sstevel@tonic-gate 
1258*0Sstevel@tonic-gate 	case sizeof (sin_t):
1259*0Sstevel@tonic-gate 		ASSERT(icmp->icmp_family == AF_INET);
1260*0Sstevel@tonic-gate 		/* Append a request for an IRE */
1261*0Sstevel@tonic-gate 		mp->b_cont = allocb(sizeof (ire_t), BPRI_HI);
1262*0Sstevel@tonic-gate 		if (!mp->b_cont) {
1263*0Sstevel@tonic-gate 			freemsg(mp);
1264*0Sstevel@tonic-gate 			return (NULL);
1265*0Sstevel@tonic-gate 		}
1266*0Sstevel@tonic-gate 		mp->b_cont->b_wptr += sizeof (ire_t);
1267*0Sstevel@tonic-gate 		mp->b_cont->b_datap->db_type = IRE_DB_REQ_TYPE;
1268*0Sstevel@tonic-gate 
1269*0Sstevel@tonic-gate 		sin = (sin_t *)cp;
1270*0Sstevel@tonic-gate 		*sin = sin_null;
1271*0Sstevel@tonic-gate 		sin->sin_family = AF_INET;
1272*0Sstevel@tonic-gate 		sin->sin_addr.s_addr = V4_PART_OF_V6(icmp->icmp_bound_v6src);
1273*0Sstevel@tonic-gate 		break;
1274*0Sstevel@tonic-gate 
1275*0Sstevel@tonic-gate 	case sizeof (sin6_t):
1276*0Sstevel@tonic-gate 		ASSERT(icmp->icmp_family == AF_INET6);
1277*0Sstevel@tonic-gate 		/* Append a request for an IRE */
1278*0Sstevel@tonic-gate 		mp->b_cont = allocb(sizeof (ire_t), BPRI_HI);
1279*0Sstevel@tonic-gate 		if (!mp->b_cont) {
1280*0Sstevel@tonic-gate 			freemsg(mp);
1281*0Sstevel@tonic-gate 			return (NULL);
1282*0Sstevel@tonic-gate 		}
1283*0Sstevel@tonic-gate 		mp->b_cont->b_wptr += sizeof (ire_t);
1284*0Sstevel@tonic-gate 		mp->b_cont->b_datap->db_type = IRE_DB_REQ_TYPE;
1285*0Sstevel@tonic-gate 
1286*0Sstevel@tonic-gate 		sin6 = (sin6_t *)cp;
1287*0Sstevel@tonic-gate 		*sin6 = sin6_null;
1288*0Sstevel@tonic-gate 		sin6->sin6_family = AF_INET6;
1289*0Sstevel@tonic-gate 		sin6->sin6_addr = icmp->icmp_bound_v6src;
1290*0Sstevel@tonic-gate 		break;
1291*0Sstevel@tonic-gate 	}
1292*0Sstevel@tonic-gate 	/* Add protocol number to end */
1293*0Sstevel@tonic-gate 	cp[addr_length] = icmp->icmp_proto;
1294*0Sstevel@tonic-gate 	mp->b_wptr = (uchar_t *)&cp[addr_length + 1];
1295*0Sstevel@tonic-gate 	return (mp);
1296*0Sstevel@tonic-gate }
1297*0Sstevel@tonic-gate 
1298*0Sstevel@tonic-gate /*
1299*0Sstevel@tonic-gate  * This is the open routine for icmp.  It allocates a icmp_t structure for
1300*0Sstevel@tonic-gate  * the stream and, on the first open of the module, creates an ND table.
1301*0Sstevel@tonic-gate  */
1302*0Sstevel@tonic-gate static int
1303*0Sstevel@tonic-gate icmp_open(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp)
1304*0Sstevel@tonic-gate {
1305*0Sstevel@tonic-gate 	int	err;
1306*0Sstevel@tonic-gate 	icmp_t	*icmp;
1307*0Sstevel@tonic-gate 
1308*0Sstevel@tonic-gate 	/* If the stream is already open, return immediately. */
1309*0Sstevel@tonic-gate 	if (q->q_ptr != NULL)
1310*0Sstevel@tonic-gate 		return (0);
1311*0Sstevel@tonic-gate 
1312*0Sstevel@tonic-gate 	/* If this is not a push of icmp as a module, fail. */
1313*0Sstevel@tonic-gate 	if (sflag != MODOPEN)
1314*0Sstevel@tonic-gate 		return (EINVAL);
1315*0Sstevel@tonic-gate 
1316*0Sstevel@tonic-gate 	/*
1317*0Sstevel@tonic-gate 	 * Defer the qprocson until everything is initialized since
1318*0Sstevel@tonic-gate 	 * we are D_MTPERQ and after qprocson the rput routine can
1319*0Sstevel@tonic-gate 	 * run. (Could do qprocson earlier since icmp currently
1320*0Sstevel@tonic-gate 	 * has an outer perimeter.)
1321*0Sstevel@tonic-gate 	 */
1322*0Sstevel@tonic-gate 
1323*0Sstevel@tonic-gate 	/*
1324*0Sstevel@tonic-gate 	 * Create a icmp_t structure for this stream and link into the
1325*0Sstevel@tonic-gate 	 * list of open streams.
1326*0Sstevel@tonic-gate 	 */
1327*0Sstevel@tonic-gate 	err = mi_open_comm(&icmp_g_head, sizeof (icmp_t), q, devp,
1328*0Sstevel@tonic-gate 	    flag, sflag, credp);
1329*0Sstevel@tonic-gate 	if (err)
1330*0Sstevel@tonic-gate 		return (err);
1331*0Sstevel@tonic-gate 
1332*0Sstevel@tonic-gate 	/*
1333*0Sstevel@tonic-gate 	 * The receive hiwat is only looked at on the stream head queue.
1334*0Sstevel@tonic-gate 	 * Store in q_hiwat in order to return on SO_RCVBUF getsockopts.
1335*0Sstevel@tonic-gate 	 */
1336*0Sstevel@tonic-gate 	q->q_hiwat = icmp_recv_hiwat;
1337*0Sstevel@tonic-gate 
1338*0Sstevel@tonic-gate 	/* Set the initial state of the stream and the privilege status. */
1339*0Sstevel@tonic-gate 	icmp = (icmp_t *)q->q_ptr;
1340*0Sstevel@tonic-gate 	icmp->icmp_state = TS_UNBND;
1341*0Sstevel@tonic-gate 	icmp->icmp_multicast_ttl = IP_DEFAULT_MULTICAST_TTL;
1342*0Sstevel@tonic-gate 	icmp->icmp_multicast_loop = IP_DEFAULT_MULTICAST_LOOP;
1343*0Sstevel@tonic-gate 	icmp->icmp_filter = NULL;
1344*0Sstevel@tonic-gate 
1345*0Sstevel@tonic-gate 	icmp->icmp_credp = credp;
1346*0Sstevel@tonic-gate 	crhold(credp);
1347*0Sstevel@tonic-gate 
1348*0Sstevel@tonic-gate 	icmp->icmp_zoneid = getzoneid();
1349*0Sstevel@tonic-gate 
1350*0Sstevel@tonic-gate 	if (getmajor(*devp) == (major_t)ICMP6_MAJ) {
1351*0Sstevel@tonic-gate 		icmp->icmp_ipversion = IPV6_VERSION;
1352*0Sstevel@tonic-gate 		icmp->icmp_family = AF_INET6;
1353*0Sstevel@tonic-gate 		/* May be changed by a SO_PROTOTYPE socket option. */
1354*0Sstevel@tonic-gate 		icmp->icmp_proto = IPPROTO_ICMPV6;
1355*0Sstevel@tonic-gate 		icmp->icmp_checksum_off = 2;	/* Offset for icmp6_cksum */
1356*0Sstevel@tonic-gate 		icmp->icmp_max_hdr_len = IPV6_HDR_LEN;
1357*0Sstevel@tonic-gate 		icmp->icmp_ttl = (uint8_t)icmp_ipv6_hoplimit;
1358*0Sstevel@tonic-gate 	} else {
1359*0Sstevel@tonic-gate 		icmp->icmp_ipversion = IPV4_VERSION;
1360*0Sstevel@tonic-gate 		icmp->icmp_family = AF_INET;
1361*0Sstevel@tonic-gate 		/* May be changed by a SO_PROTOTYPE socket option. */
1362*0Sstevel@tonic-gate 		icmp->icmp_proto = IPPROTO_ICMP;
1363*0Sstevel@tonic-gate 		icmp->icmp_max_hdr_len = IP_SIMPLE_HDR_LENGTH;
1364*0Sstevel@tonic-gate 		icmp->icmp_ttl = (uint8_t)icmp_ipv4_ttl;
1365*0Sstevel@tonic-gate 	}
1366*0Sstevel@tonic-gate 	qprocson(q);
1367*0Sstevel@tonic-gate 
1368*0Sstevel@tonic-gate 	/*
1369*0Sstevel@tonic-gate 	 * Check if icmp is being I_PUSHed by a non-privileged user.
1370*0Sstevel@tonic-gate 	 * If so, we set icmp_restricted to indicate that only MIB
1371*0Sstevel@tonic-gate 	 * traffic may pass.
1372*0Sstevel@tonic-gate 	 */
1373*0Sstevel@tonic-gate 	if (secpolicy_net_icmpaccess(credp) != 0) {
1374*0Sstevel@tonic-gate 		icmp->icmp_restricted = 1;
1375*0Sstevel@tonic-gate 	}
1376*0Sstevel@tonic-gate 
1377*0Sstevel@tonic-gate 	/*
1378*0Sstevel@tonic-gate 	 * The transmit hiwat is only looked at on IP's queue.
1379*0Sstevel@tonic-gate 	 * Store in q_hiwat in order to return on SO_SNDBUF
1380*0Sstevel@tonic-gate 	 * getsockopts.
1381*0Sstevel@tonic-gate 	 */
1382*0Sstevel@tonic-gate 	WR(q)->q_hiwat = icmp_xmit_hiwat;
1383*0Sstevel@tonic-gate 	WR(q)->q_next->q_hiwat = WR(q)->q_hiwat;
1384*0Sstevel@tonic-gate 	WR(q)->q_lowat = icmp_xmit_lowat;
1385*0Sstevel@tonic-gate 	WR(q)->q_next->q_lowat = WR(q)->q_lowat;
1386*0Sstevel@tonic-gate 
1387*0Sstevel@tonic-gate 	if (icmp->icmp_family == AF_INET6) {
1388*0Sstevel@tonic-gate 		/* Build initial header template for transmit */
1389*0Sstevel@tonic-gate 		int error;
1390*0Sstevel@tonic-gate 
1391*0Sstevel@tonic-gate 		error = icmp_build_hdrs(q, icmp);
1392*0Sstevel@tonic-gate 		if (error != 0) {
1393*0Sstevel@tonic-gate 			(void) icmp_close(q);
1394*0Sstevel@tonic-gate 			return (error);
1395*0Sstevel@tonic-gate 		}
1396*0Sstevel@tonic-gate 	}
1397*0Sstevel@tonic-gate 	/* Set the Stream head write offset. */
1398*0Sstevel@tonic-gate 	(void) mi_set_sth_wroff(q, icmp->icmp_max_hdr_len + icmp_wroff_extra);
1399*0Sstevel@tonic-gate 	(void) mi_set_sth_hiwat(q, q->q_hiwat);
1400*0Sstevel@tonic-gate 
1401*0Sstevel@tonic-gate 	return (0);
1402*0Sstevel@tonic-gate }
1403*0Sstevel@tonic-gate 
1404*0Sstevel@tonic-gate /*
1405*0Sstevel@tonic-gate  * Which ICMP options OK to set through T_UNITDATA_REQ...
1406*0Sstevel@tonic-gate  */
1407*0Sstevel@tonic-gate /* ARGSUSED */
1408*0Sstevel@tonic-gate static boolean_t
1409*0Sstevel@tonic-gate icmp_opt_allow_udr_set(t_scalar_t level, t_scalar_t name)
1410*0Sstevel@tonic-gate {
1411*0Sstevel@tonic-gate 	return (B_TRUE);
1412*0Sstevel@tonic-gate }
1413*0Sstevel@tonic-gate 
1414*0Sstevel@tonic-gate /*
1415*0Sstevel@tonic-gate  * This routine gets default values of certain options whose default
1416*0Sstevel@tonic-gate  * values are maintained by protcol specific code
1417*0Sstevel@tonic-gate  */
1418*0Sstevel@tonic-gate /* ARGSUSED */
1419*0Sstevel@tonic-gate int
1420*0Sstevel@tonic-gate icmp_opt_default(queue_t *q, int level, int name, uchar_t *ptr)
1421*0Sstevel@tonic-gate {
1422*0Sstevel@tonic-gate 	int *i1 = (int *)ptr;
1423*0Sstevel@tonic-gate 
1424*0Sstevel@tonic-gate 	switch (level) {
1425*0Sstevel@tonic-gate 	case IPPROTO_IP:
1426*0Sstevel@tonic-gate 		switch (name) {
1427*0Sstevel@tonic-gate 		case IP_MULTICAST_TTL:
1428*0Sstevel@tonic-gate 			*ptr = (uchar_t)IP_DEFAULT_MULTICAST_TTL;
1429*0Sstevel@tonic-gate 			return (sizeof (uchar_t));
1430*0Sstevel@tonic-gate 		case IP_MULTICAST_LOOP:
1431*0Sstevel@tonic-gate 			*ptr = (uchar_t)IP_DEFAULT_MULTICAST_LOOP;
1432*0Sstevel@tonic-gate 			return (sizeof (uchar_t));
1433*0Sstevel@tonic-gate 		}
1434*0Sstevel@tonic-gate 		break;
1435*0Sstevel@tonic-gate 	case IPPROTO_IPV6:
1436*0Sstevel@tonic-gate 		switch (name) {
1437*0Sstevel@tonic-gate 		case IPV6_MULTICAST_HOPS:
1438*0Sstevel@tonic-gate 			*i1 = IP_DEFAULT_MULTICAST_TTL;
1439*0Sstevel@tonic-gate 			return (sizeof (int));
1440*0Sstevel@tonic-gate 		case IPV6_MULTICAST_LOOP:
1441*0Sstevel@tonic-gate 			*i1 = IP_DEFAULT_MULTICAST_LOOP;
1442*0Sstevel@tonic-gate 			return (sizeof (int));
1443*0Sstevel@tonic-gate 		case IPV6_UNICAST_HOPS:
1444*0Sstevel@tonic-gate 			*i1 = icmp_ipv6_hoplimit;
1445*0Sstevel@tonic-gate 			return (sizeof (int));
1446*0Sstevel@tonic-gate 		}
1447*0Sstevel@tonic-gate 		break;
1448*0Sstevel@tonic-gate 	case IPPROTO_ICMPV6:
1449*0Sstevel@tonic-gate 		switch (name) {
1450*0Sstevel@tonic-gate 		case ICMP6_FILTER:
1451*0Sstevel@tonic-gate 			/* Make it look like "pass all" */
1452*0Sstevel@tonic-gate 			ICMP6_FILTER_SETPASSALL((icmp6_filter_t *)ptr);
1453*0Sstevel@tonic-gate 			return (sizeof (icmp6_filter_t));
1454*0Sstevel@tonic-gate 		}
1455*0Sstevel@tonic-gate 		break;
1456*0Sstevel@tonic-gate 	}
1457*0Sstevel@tonic-gate 	return (-1);
1458*0Sstevel@tonic-gate }
1459*0Sstevel@tonic-gate 
1460*0Sstevel@tonic-gate /*
1461*0Sstevel@tonic-gate  * This routine retrieves the current status of socket options.
1462*0Sstevel@tonic-gate  * It returns the size of the option retrieved.
1463*0Sstevel@tonic-gate  */
1464*0Sstevel@tonic-gate int
1465*0Sstevel@tonic-gate icmp_opt_get(queue_t *q, int level, int name, uchar_t *ptr)
1466*0Sstevel@tonic-gate {
1467*0Sstevel@tonic-gate 	icmp_t	*icmp = (icmp_t *)q->q_ptr;
1468*0Sstevel@tonic-gate 	int	*i1 = (int *)ptr;
1469*0Sstevel@tonic-gate 	ip6_pkt_t	*ipp = &icmp->icmp_sticky_ipp;
1470*0Sstevel@tonic-gate 
1471*0Sstevel@tonic-gate 	switch (level) {
1472*0Sstevel@tonic-gate 	case SOL_SOCKET:
1473*0Sstevel@tonic-gate 		switch (name) {
1474*0Sstevel@tonic-gate 		case SO_DEBUG:
1475*0Sstevel@tonic-gate 			*i1 = icmp->icmp_debug;
1476*0Sstevel@tonic-gate 			break;
1477*0Sstevel@tonic-gate 		case SO_TYPE:
1478*0Sstevel@tonic-gate 			*i1 = SOCK_RAW;
1479*0Sstevel@tonic-gate 			break;
1480*0Sstevel@tonic-gate 		case SO_PROTOTYPE:
1481*0Sstevel@tonic-gate 			*i1 = icmp->icmp_proto;
1482*0Sstevel@tonic-gate 			break;
1483*0Sstevel@tonic-gate 		case SO_REUSEADDR:
1484*0Sstevel@tonic-gate 			*i1 = icmp->icmp_reuseaddr;
1485*0Sstevel@tonic-gate 			break;
1486*0Sstevel@tonic-gate 
1487*0Sstevel@tonic-gate 		/*
1488*0Sstevel@tonic-gate 		 * The following three items are available here,
1489*0Sstevel@tonic-gate 		 * but are only meaningful to IP.
1490*0Sstevel@tonic-gate 		 */
1491*0Sstevel@tonic-gate 		case SO_DONTROUTE:
1492*0Sstevel@tonic-gate 			*i1 = icmp->icmp_dontroute;
1493*0Sstevel@tonic-gate 			break;
1494*0Sstevel@tonic-gate 		case SO_USELOOPBACK:
1495*0Sstevel@tonic-gate 			*i1 = icmp->icmp_useloopback;
1496*0Sstevel@tonic-gate 			break;
1497*0Sstevel@tonic-gate 		case SO_BROADCAST:
1498*0Sstevel@tonic-gate 			*i1 = icmp->icmp_broadcast;
1499*0Sstevel@tonic-gate 			break;
1500*0Sstevel@tonic-gate 
1501*0Sstevel@tonic-gate 		case SO_SNDBUF:
1502*0Sstevel@tonic-gate 			ASSERT(q->q_hiwat <= INT_MAX);
1503*0Sstevel@tonic-gate 			*i1 = (int)q->q_hiwat;
1504*0Sstevel@tonic-gate 			break;
1505*0Sstevel@tonic-gate 		case SO_RCVBUF:
1506*0Sstevel@tonic-gate 			ASSERT(RD(q)->q_hiwat <= INT_MAX);
1507*0Sstevel@tonic-gate 			*i1 = (int)RD(q)->q_hiwat;
1508*0Sstevel@tonic-gate 			break;
1509*0Sstevel@tonic-gate 		case SO_DGRAM_ERRIND:
1510*0Sstevel@tonic-gate 			*i1 = icmp->icmp_dgram_errind;
1511*0Sstevel@tonic-gate 			break;
1512*0Sstevel@tonic-gate 		/*
1513*0Sstevel@tonic-gate 		 * Following three not meaningful for icmp
1514*0Sstevel@tonic-gate 		 * Action is same as "default" to which we fallthrough
1515*0Sstevel@tonic-gate 		 * so we keep them in comments.
1516*0Sstevel@tonic-gate 		 * case SO_LINGER:
1517*0Sstevel@tonic-gate 		 * case SO_KEEPALIVE:
1518*0Sstevel@tonic-gate 		 * case SO_OOBINLINE:
1519*0Sstevel@tonic-gate 		 */
1520*0Sstevel@tonic-gate 		default:
1521*0Sstevel@tonic-gate 			return (-1);
1522*0Sstevel@tonic-gate 		}
1523*0Sstevel@tonic-gate 		break;
1524*0Sstevel@tonic-gate 	case IPPROTO_IP:
1525*0Sstevel@tonic-gate 		/*
1526*0Sstevel@tonic-gate 		 * Only allow IPv4 option processing on IPv4 sockets.
1527*0Sstevel@tonic-gate 		 */
1528*0Sstevel@tonic-gate 		if (icmp->icmp_family != AF_INET)
1529*0Sstevel@tonic-gate 			return (-1);
1530*0Sstevel@tonic-gate 
1531*0Sstevel@tonic-gate 		switch (name) {
1532*0Sstevel@tonic-gate 		case IP_OPTIONS:
1533*0Sstevel@tonic-gate 		case T_IP_OPTIONS:
1534*0Sstevel@tonic-gate 			/* Options are passed up with each packet */
1535*0Sstevel@tonic-gate 			return (0);
1536*0Sstevel@tonic-gate 		case IP_HDRINCL:
1537*0Sstevel@tonic-gate 			*i1 = (int)icmp->icmp_hdrincl;
1538*0Sstevel@tonic-gate 			break;
1539*0Sstevel@tonic-gate 		case IP_TOS:
1540*0Sstevel@tonic-gate 		case T_IP_TOS:
1541*0Sstevel@tonic-gate 			*i1 = (int)icmp->icmp_type_of_service;
1542*0Sstevel@tonic-gate 			break;
1543*0Sstevel@tonic-gate 		case IP_TTL:
1544*0Sstevel@tonic-gate 			*i1 = (int)icmp->icmp_ttl;
1545*0Sstevel@tonic-gate 			break;
1546*0Sstevel@tonic-gate 		case IP_MULTICAST_IF:
1547*0Sstevel@tonic-gate 			/* 0 address if not set */
1548*0Sstevel@tonic-gate 			*(ipaddr_t *)ptr = icmp->icmp_multicast_if_addr;
1549*0Sstevel@tonic-gate 			return (sizeof (ipaddr_t));
1550*0Sstevel@tonic-gate 		case IP_MULTICAST_TTL:
1551*0Sstevel@tonic-gate 			*(uchar_t *)ptr = icmp->icmp_multicast_ttl;
1552*0Sstevel@tonic-gate 			return (sizeof (uchar_t));
1553*0Sstevel@tonic-gate 		case IP_MULTICAST_LOOP:
1554*0Sstevel@tonic-gate 			*ptr = icmp->icmp_multicast_loop;
1555*0Sstevel@tonic-gate 			return (sizeof (uint8_t));
1556*0Sstevel@tonic-gate 		case IP_BOUND_IF:
1557*0Sstevel@tonic-gate 			/* Zero if not set */
1558*0Sstevel@tonic-gate 			*i1 = icmp->icmp_bound_if;
1559*0Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
1560*0Sstevel@tonic-gate 		case IP_UNSPEC_SRC:
1561*0Sstevel@tonic-gate 			*ptr = icmp->icmp_unspec_source;
1562*0Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
1563*0Sstevel@tonic-gate 		case IP_XMIT_IF:
1564*0Sstevel@tonic-gate 			*i1 = icmp->icmp_xmit_if;
1565*0Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
1566*0Sstevel@tonic-gate 		case IP_RECVIF:
1567*0Sstevel@tonic-gate 			*ptr = icmp->icmp_recvif;
1568*0Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
1569*0Sstevel@tonic-gate 		/*
1570*0Sstevel@tonic-gate 		 * Cannot "get" the value of following options
1571*0Sstevel@tonic-gate 		 * at this level. Action is same as "default" to
1572*0Sstevel@tonic-gate 		 * which we fallthrough so we keep them in comments.
1573*0Sstevel@tonic-gate 		 *
1574*0Sstevel@tonic-gate 		 * case IP_ADD_MEMBERSHIP:
1575*0Sstevel@tonic-gate 		 * case IP_DROP_MEMBERSHIP:
1576*0Sstevel@tonic-gate 		 * case IP_BLOCK_SOURCE:
1577*0Sstevel@tonic-gate 		 * case IP_UNBLOCK_SOURCE:
1578*0Sstevel@tonic-gate 		 * case IP_ADD_SOURCE_MEMBERSHIP:
1579*0Sstevel@tonic-gate 		 * case IP_DROP_SOURCE_MEMBERSHIP:
1580*0Sstevel@tonic-gate 		 * case MCAST_JOIN_GROUP:
1581*0Sstevel@tonic-gate 		 * case MCAST_LEAVE_GROUP:
1582*0Sstevel@tonic-gate 		 * case MCAST_BLOCK_SOURCE:
1583*0Sstevel@tonic-gate 		 * case MCAST_UNBLOCK_SOURCE:
1584*0Sstevel@tonic-gate 		 * case MCAST_JOIN_SOURCE_GROUP:
1585*0Sstevel@tonic-gate 		 * case MCAST_LEAVE_SOURCE_GROUP:
1586*0Sstevel@tonic-gate 		 * case MRT_INIT:
1587*0Sstevel@tonic-gate 		 * case MRT_DONE:
1588*0Sstevel@tonic-gate 		 * case MRT_ADD_VIF:
1589*0Sstevel@tonic-gate 		 * case MRT_DEL_VIF:
1590*0Sstevel@tonic-gate 		 * case MRT_ADD_MFC:
1591*0Sstevel@tonic-gate 		 * case MRT_DEL_MFC:
1592*0Sstevel@tonic-gate 		 * case MRT_VERSION:
1593*0Sstevel@tonic-gate 		 * case MRT_ASSERT:
1594*0Sstevel@tonic-gate 		 * case IP_SEC_OPT:
1595*0Sstevel@tonic-gate 		 * case IP_DONTFAILOVER_IF:
1596*0Sstevel@tonic-gate 		 */
1597*0Sstevel@tonic-gate 		default:
1598*0Sstevel@tonic-gate 			return (-1);
1599*0Sstevel@tonic-gate 		}
1600*0Sstevel@tonic-gate 		break;
1601*0Sstevel@tonic-gate 	case IPPROTO_IPV6:
1602*0Sstevel@tonic-gate 		/*
1603*0Sstevel@tonic-gate 		 * Only allow IPv6 option processing on native IPv6 sockets.
1604*0Sstevel@tonic-gate 		 */
1605*0Sstevel@tonic-gate 		if (icmp->icmp_family != AF_INET6)
1606*0Sstevel@tonic-gate 			return (-1);
1607*0Sstevel@tonic-gate 		switch (name) {
1608*0Sstevel@tonic-gate 		case IPV6_UNICAST_HOPS:
1609*0Sstevel@tonic-gate 			*i1 = (unsigned int)icmp->icmp_ttl;
1610*0Sstevel@tonic-gate 			break;
1611*0Sstevel@tonic-gate 		case IPV6_MULTICAST_IF:
1612*0Sstevel@tonic-gate 			/* 0 index if not set */
1613*0Sstevel@tonic-gate 			*i1 = icmp->icmp_multicast_if_index;
1614*0Sstevel@tonic-gate 			break;
1615*0Sstevel@tonic-gate 		case IPV6_MULTICAST_HOPS:
1616*0Sstevel@tonic-gate 			*i1 = icmp->icmp_multicast_ttl;
1617*0Sstevel@tonic-gate 			break;
1618*0Sstevel@tonic-gate 		case IPV6_MULTICAST_LOOP:
1619*0Sstevel@tonic-gate 			*i1 = icmp->icmp_multicast_loop;
1620*0Sstevel@tonic-gate 			break;
1621*0Sstevel@tonic-gate 		case IPV6_BOUND_IF:
1622*0Sstevel@tonic-gate 			/* Zero if not set */
1623*0Sstevel@tonic-gate 			*i1 = icmp->icmp_bound_if;
1624*0Sstevel@tonic-gate 			break;
1625*0Sstevel@tonic-gate 		case IPV6_UNSPEC_SRC:
1626*0Sstevel@tonic-gate 			*i1 = icmp->icmp_unspec_source;
1627*0Sstevel@tonic-gate 			break;
1628*0Sstevel@tonic-gate 		case IPV6_CHECKSUM:
1629*0Sstevel@tonic-gate 			/*
1630*0Sstevel@tonic-gate 			 * Return offset or -1 if no checksum offset.
1631*0Sstevel@tonic-gate 			 * Does not apply to IPPROTO_ICMPV6
1632*0Sstevel@tonic-gate 			 */
1633*0Sstevel@tonic-gate 			if (icmp->icmp_proto == IPPROTO_ICMPV6)
1634*0Sstevel@tonic-gate 				return (-1);
1635*0Sstevel@tonic-gate 
1636*0Sstevel@tonic-gate 			if (icmp->icmp_raw_checksum) {
1637*0Sstevel@tonic-gate 				*i1 = icmp->icmp_checksum_off;
1638*0Sstevel@tonic-gate 			} else {
1639*0Sstevel@tonic-gate 				*i1 = -1;
1640*0Sstevel@tonic-gate 			}
1641*0Sstevel@tonic-gate 			break;
1642*0Sstevel@tonic-gate 		case IPV6_JOIN_GROUP:
1643*0Sstevel@tonic-gate 		case IPV6_LEAVE_GROUP:
1644*0Sstevel@tonic-gate 		case MCAST_JOIN_GROUP:
1645*0Sstevel@tonic-gate 		case MCAST_LEAVE_GROUP:
1646*0Sstevel@tonic-gate 		case MCAST_BLOCK_SOURCE:
1647*0Sstevel@tonic-gate 		case MCAST_UNBLOCK_SOURCE:
1648*0Sstevel@tonic-gate 		case MCAST_JOIN_SOURCE_GROUP:
1649*0Sstevel@tonic-gate 		case MCAST_LEAVE_SOURCE_GROUP:
1650*0Sstevel@tonic-gate 			/* cannot "get" the value for these */
1651*0Sstevel@tonic-gate 			return (-1);
1652*0Sstevel@tonic-gate 		case IPV6_RECVPKTINFO:
1653*0Sstevel@tonic-gate 			*i1 = icmp->icmp_ipv6_recvpktinfo;
1654*0Sstevel@tonic-gate 			break;
1655*0Sstevel@tonic-gate 		case IPV6_RECVTCLASS:
1656*0Sstevel@tonic-gate 			*i1 = icmp->icmp_ipv6_recvtclass;
1657*0Sstevel@tonic-gate 			break;
1658*0Sstevel@tonic-gate 		case IPV6_RECVPATHMTU:
1659*0Sstevel@tonic-gate 			*i1 = icmp->icmp_ipv6_recvpathmtu;
1660*0Sstevel@tonic-gate 			break;
1661*0Sstevel@tonic-gate 		case IPV6_V6ONLY:
1662*0Sstevel@tonic-gate 			*i1 = 1;
1663*0Sstevel@tonic-gate 			break;
1664*0Sstevel@tonic-gate 		case IPV6_RECVHOPLIMIT:
1665*0Sstevel@tonic-gate 			*i1 = icmp->icmp_ipv6_recvhoplimit;
1666*0Sstevel@tonic-gate 			break;
1667*0Sstevel@tonic-gate 		case IPV6_RECVHOPOPTS:
1668*0Sstevel@tonic-gate 			*i1 = icmp->icmp_ipv6_recvhopopts;
1669*0Sstevel@tonic-gate 			break;
1670*0Sstevel@tonic-gate 		case IPV6_RECVDSTOPTS:
1671*0Sstevel@tonic-gate 			*i1 = icmp->icmp_ipv6_recvdstopts;
1672*0Sstevel@tonic-gate 			break;
1673*0Sstevel@tonic-gate 		case _OLD_IPV6_RECVDSTOPTS:
1674*0Sstevel@tonic-gate 			*i1 = icmp->icmp_old_ipv6_recvdstopts;
1675*0Sstevel@tonic-gate 			break;
1676*0Sstevel@tonic-gate 		case IPV6_RECVRTHDRDSTOPTS:
1677*0Sstevel@tonic-gate 			*i1 = icmp->icmp_ipv6_recvrtdstopts;
1678*0Sstevel@tonic-gate 			break;
1679*0Sstevel@tonic-gate 		case IPV6_RECVRTHDR:
1680*0Sstevel@tonic-gate 			*i1 = icmp->icmp_ipv6_recvrthdr;
1681*0Sstevel@tonic-gate 			break;
1682*0Sstevel@tonic-gate 		case IPV6_PKTINFO: {
1683*0Sstevel@tonic-gate 			/* XXX assumes that caller has room for max size! */
1684*0Sstevel@tonic-gate 			struct in6_pktinfo *pkti;
1685*0Sstevel@tonic-gate 
1686*0Sstevel@tonic-gate 			pkti = (struct in6_pktinfo *)ptr;
1687*0Sstevel@tonic-gate 			if (ipp->ipp_fields & IPPF_IFINDEX)
1688*0Sstevel@tonic-gate 				pkti->ipi6_ifindex = ipp->ipp_ifindex;
1689*0Sstevel@tonic-gate 			else
1690*0Sstevel@tonic-gate 				pkti->ipi6_ifindex = 0;
1691*0Sstevel@tonic-gate 			if (ipp->ipp_fields & IPPF_ADDR)
1692*0Sstevel@tonic-gate 				pkti->ipi6_addr = ipp->ipp_addr;
1693*0Sstevel@tonic-gate 			else
1694*0Sstevel@tonic-gate 				pkti->ipi6_addr = ipv6_all_zeros;
1695*0Sstevel@tonic-gate 			return (sizeof (struct in6_pktinfo));
1696*0Sstevel@tonic-gate 		}
1697*0Sstevel@tonic-gate 		case IPV6_HOPLIMIT:
1698*0Sstevel@tonic-gate 			if (ipp->ipp_fields & IPPF_HOPLIMIT)
1699*0Sstevel@tonic-gate 				*i1 = ipp->ipp_hoplimit;
1700*0Sstevel@tonic-gate 			else
1701*0Sstevel@tonic-gate 				*i1 = -1; /* Not set */
1702*0Sstevel@tonic-gate 			break;
1703*0Sstevel@tonic-gate 		case IPV6_NEXTHOP: {
1704*0Sstevel@tonic-gate 			sin6_t *sin6 = (sin6_t *)ptr;
1705*0Sstevel@tonic-gate 
1706*0Sstevel@tonic-gate 			if (!(ipp->ipp_fields & IPPF_NEXTHOP))
1707*0Sstevel@tonic-gate 				return (0);
1708*0Sstevel@tonic-gate 			*sin6 = sin6_null;
1709*0Sstevel@tonic-gate 			sin6->sin6_family = AF_INET6;
1710*0Sstevel@tonic-gate 			sin6->sin6_addr = ipp->ipp_nexthop;
1711*0Sstevel@tonic-gate 			return (sizeof (sin6_t));
1712*0Sstevel@tonic-gate 		}
1713*0Sstevel@tonic-gate 		case IPV6_HOPOPTS:
1714*0Sstevel@tonic-gate 			if (!(ipp->ipp_fields & IPPF_HOPOPTS))
1715*0Sstevel@tonic-gate 				return (0);
1716*0Sstevel@tonic-gate 			bcopy(ipp->ipp_hopopts, ptr, ipp->ipp_hopoptslen);
1717*0Sstevel@tonic-gate 			return (ipp->ipp_hopoptslen);
1718*0Sstevel@tonic-gate 		case IPV6_RTHDRDSTOPTS:
1719*0Sstevel@tonic-gate 			if (!(ipp->ipp_fields & IPPF_RTDSTOPTS))
1720*0Sstevel@tonic-gate 				return (0);
1721*0Sstevel@tonic-gate 			bcopy(ipp->ipp_rtdstopts, ptr, ipp->ipp_rtdstoptslen);
1722*0Sstevel@tonic-gate 			return (ipp->ipp_rtdstoptslen);
1723*0Sstevel@tonic-gate 		case IPV6_RTHDR:
1724*0Sstevel@tonic-gate 			if (!(ipp->ipp_fields & IPPF_RTHDR))
1725*0Sstevel@tonic-gate 				return (0);
1726*0Sstevel@tonic-gate 			bcopy(ipp->ipp_rthdr, ptr, ipp->ipp_rthdrlen);
1727*0Sstevel@tonic-gate 			return (ipp->ipp_rthdrlen);
1728*0Sstevel@tonic-gate 		case IPV6_DSTOPTS:
1729*0Sstevel@tonic-gate 			if (!(ipp->ipp_fields & IPPF_DSTOPTS))
1730*0Sstevel@tonic-gate 				return (0);
1731*0Sstevel@tonic-gate 			bcopy(ipp->ipp_dstopts, ptr, ipp->ipp_dstoptslen);
1732*0Sstevel@tonic-gate 			return (ipp->ipp_dstoptslen);
1733*0Sstevel@tonic-gate 		case IPV6_PATHMTU:
1734*0Sstevel@tonic-gate 			if (!(ipp->ipp_fields & IPPF_PATHMTU))
1735*0Sstevel@tonic-gate 				return (0);
1736*0Sstevel@tonic-gate 
1737*0Sstevel@tonic-gate 			return (ip_fill_mtuinfo(&icmp->icmp_v6dst, 0,
1738*0Sstevel@tonic-gate 				(struct ip6_mtuinfo *)ptr));
1739*0Sstevel@tonic-gate 		case IPV6_TCLASS:
1740*0Sstevel@tonic-gate 			if (ipp->ipp_fields & IPPF_TCLASS)
1741*0Sstevel@tonic-gate 				*i1 = ipp->ipp_tclass;
1742*0Sstevel@tonic-gate 			else
1743*0Sstevel@tonic-gate 				*i1 = IPV6_FLOW_TCLASS(
1744*0Sstevel@tonic-gate 				    IPV6_DEFAULT_VERS_AND_FLOW);
1745*0Sstevel@tonic-gate 			break;
1746*0Sstevel@tonic-gate 		default:
1747*0Sstevel@tonic-gate 			return (-1);
1748*0Sstevel@tonic-gate 		}
1749*0Sstevel@tonic-gate 		break;
1750*0Sstevel@tonic-gate 	case IPPROTO_ICMPV6:
1751*0Sstevel@tonic-gate 		/*
1752*0Sstevel@tonic-gate 		 * Only allow IPv6 option processing on native IPv6 sockets.
1753*0Sstevel@tonic-gate 		 */
1754*0Sstevel@tonic-gate 		if (icmp->icmp_family != AF_INET6)
1755*0Sstevel@tonic-gate 			return (-1);
1756*0Sstevel@tonic-gate 
1757*0Sstevel@tonic-gate 		if (icmp->icmp_proto != IPPROTO_ICMPV6)
1758*0Sstevel@tonic-gate 			return (-1);
1759*0Sstevel@tonic-gate 
1760*0Sstevel@tonic-gate 		switch (name) {
1761*0Sstevel@tonic-gate 		case ICMP6_FILTER:
1762*0Sstevel@tonic-gate 			if (icmp->icmp_filter == NULL) {
1763*0Sstevel@tonic-gate 				/* Make it look like "pass all" */
1764*0Sstevel@tonic-gate 				ICMP6_FILTER_SETPASSALL((icmp6_filter_t *)ptr);
1765*0Sstevel@tonic-gate 			} else {
1766*0Sstevel@tonic-gate 				(void) bcopy(icmp->icmp_filter, ptr,
1767*0Sstevel@tonic-gate 				    sizeof (icmp6_filter_t));
1768*0Sstevel@tonic-gate 			}
1769*0Sstevel@tonic-gate 			return (sizeof (icmp6_filter_t));
1770*0Sstevel@tonic-gate 		default:
1771*0Sstevel@tonic-gate 			return (-1);
1772*0Sstevel@tonic-gate 		}
1773*0Sstevel@tonic-gate 	default:
1774*0Sstevel@tonic-gate 		return (-1);
1775*0Sstevel@tonic-gate 	}
1776*0Sstevel@tonic-gate 	return (sizeof (int));
1777*0Sstevel@tonic-gate }
1778*0Sstevel@tonic-gate 
1779*0Sstevel@tonic-gate /* This routine sets socket options. */
1780*0Sstevel@tonic-gate /* ARGSUSED */
1781*0Sstevel@tonic-gate int
1782*0Sstevel@tonic-gate icmp_opt_set(queue_t *q, uint_t optset_context, int level, int name,
1783*0Sstevel@tonic-gate     uint_t inlen, uchar_t *invalp, uint_t *outlenp, uchar_t *outvalp,
1784*0Sstevel@tonic-gate     void *thisdg_attrs, cred_t *cr, mblk_t *mblk)
1785*0Sstevel@tonic-gate {
1786*0Sstevel@tonic-gate 	icmp_t	*icmp = (icmp_t *)q->q_ptr;
1787*0Sstevel@tonic-gate 	int	*i1 = (int *)invalp;
1788*0Sstevel@tonic-gate 	boolean_t onoff = (*i1 == 0) ? 0 : 1;
1789*0Sstevel@tonic-gate 	boolean_t checkonly;
1790*0Sstevel@tonic-gate 	int	error;
1791*0Sstevel@tonic-gate 
1792*0Sstevel@tonic-gate 	switch (optset_context) {
1793*0Sstevel@tonic-gate 	case SETFN_OPTCOM_CHECKONLY:
1794*0Sstevel@tonic-gate 		checkonly = B_TRUE;
1795*0Sstevel@tonic-gate 		/*
1796*0Sstevel@tonic-gate 		 * Note: Implies T_CHECK semantics for T_OPTCOM_REQ
1797*0Sstevel@tonic-gate 		 * inlen != 0 implies value supplied and
1798*0Sstevel@tonic-gate 		 * 	we have to "pretend" to set it.
1799*0Sstevel@tonic-gate 		 * inlen == 0 implies that there is no
1800*0Sstevel@tonic-gate 		 * 	value part in T_CHECK request and just validation
1801*0Sstevel@tonic-gate 		 * done elsewhere should be enough, we just return here.
1802*0Sstevel@tonic-gate 		 */
1803*0Sstevel@tonic-gate 		if (inlen == 0) {
1804*0Sstevel@tonic-gate 			*outlenp = 0;
1805*0Sstevel@tonic-gate 			return (0);
1806*0Sstevel@tonic-gate 		}
1807*0Sstevel@tonic-gate 		break;
1808*0Sstevel@tonic-gate 	case SETFN_OPTCOM_NEGOTIATE:
1809*0Sstevel@tonic-gate 		checkonly = B_FALSE;
1810*0Sstevel@tonic-gate 		break;
1811*0Sstevel@tonic-gate 	case SETFN_UD_NEGOTIATE:
1812*0Sstevel@tonic-gate 	case SETFN_CONN_NEGOTIATE:
1813*0Sstevel@tonic-gate 		checkonly = B_FALSE;
1814*0Sstevel@tonic-gate 		/*
1815*0Sstevel@tonic-gate 		 * Negotiating local and "association-related" options
1816*0Sstevel@tonic-gate 		 * through T_UNITDATA_REQ.
1817*0Sstevel@tonic-gate 		 *
1818*0Sstevel@tonic-gate 		 * Following routine can filter out ones we do not
1819*0Sstevel@tonic-gate 		 * want to be "set" this way.
1820*0Sstevel@tonic-gate 		 */
1821*0Sstevel@tonic-gate 		if (!icmp_opt_allow_udr_set(level, name)) {
1822*0Sstevel@tonic-gate 			*outlenp = 0;
1823*0Sstevel@tonic-gate 			return (EINVAL);
1824*0Sstevel@tonic-gate 		}
1825*0Sstevel@tonic-gate 		break;
1826*0Sstevel@tonic-gate 	default:
1827*0Sstevel@tonic-gate 		/*
1828*0Sstevel@tonic-gate 		 * We should never get here
1829*0Sstevel@tonic-gate 		 */
1830*0Sstevel@tonic-gate 		*outlenp = 0;
1831*0Sstevel@tonic-gate 		return (EINVAL);
1832*0Sstevel@tonic-gate 	}
1833*0Sstevel@tonic-gate 
1834*0Sstevel@tonic-gate 	ASSERT((optset_context != SETFN_OPTCOM_CHECKONLY) ||
1835*0Sstevel@tonic-gate 	    (optset_context == SETFN_OPTCOM_CHECKONLY && inlen != 0));
1836*0Sstevel@tonic-gate 
1837*0Sstevel@tonic-gate 	/*
1838*0Sstevel@tonic-gate 	 * For fixed length options, no sanity check
1839*0Sstevel@tonic-gate 	 * of passed in length is done. It is assumed *_optcom_req()
1840*0Sstevel@tonic-gate 	 * routines do the right thing.
1841*0Sstevel@tonic-gate 	 */
1842*0Sstevel@tonic-gate 
1843*0Sstevel@tonic-gate 	switch (level) {
1844*0Sstevel@tonic-gate 	case SOL_SOCKET:
1845*0Sstevel@tonic-gate 		switch (name) {
1846*0Sstevel@tonic-gate 		case SO_DEBUG:
1847*0Sstevel@tonic-gate 			if (!checkonly)
1848*0Sstevel@tonic-gate 				icmp->icmp_debug = onoff;
1849*0Sstevel@tonic-gate 			break;
1850*0Sstevel@tonic-gate 		case SO_PROTOTYPE:
1851*0Sstevel@tonic-gate 			if ((*i1 & 0xFF) != IPPROTO_ICMP &&
1852*0Sstevel@tonic-gate 			    (*i1 & 0xFF) != IPPROTO_ICMPV6 &&
1853*0Sstevel@tonic-gate 			    secpolicy_net_rawaccess(cr) != 0) {
1854*0Sstevel@tonic-gate 				*outlenp = 0;
1855*0Sstevel@tonic-gate 				return (EACCES);
1856*0Sstevel@tonic-gate 			}
1857*0Sstevel@tonic-gate 			/* Can't use IPPROTO_RAW with IPv6 */
1858*0Sstevel@tonic-gate 			if ((*i1 & 0xFF) == IPPROTO_RAW &&
1859*0Sstevel@tonic-gate 			    icmp->icmp_family == AF_INET6) {
1860*0Sstevel@tonic-gate 				*outlenp = 0;
1861*0Sstevel@tonic-gate 				return (EPROTONOSUPPORT);
1862*0Sstevel@tonic-gate 			}
1863*0Sstevel@tonic-gate 			if (checkonly) {
1864*0Sstevel@tonic-gate 				/* T_CHECK case */
1865*0Sstevel@tonic-gate 				*(int *)outvalp = (*i1 & 0xFF);
1866*0Sstevel@tonic-gate 				break;
1867*0Sstevel@tonic-gate 			}
1868*0Sstevel@tonic-gate 			icmp->icmp_proto = *i1 & 0xFF;
1869*0Sstevel@tonic-gate 			if ((icmp->icmp_proto == IPPROTO_RAW ||
1870*0Sstevel@tonic-gate 			    icmp->icmp_proto == IPPROTO_IGMP) &&
1871*0Sstevel@tonic-gate 			    icmp->icmp_family == AF_INET)
1872*0Sstevel@tonic-gate 				icmp->icmp_hdrincl = 1;
1873*0Sstevel@tonic-gate 			else
1874*0Sstevel@tonic-gate 				icmp->icmp_hdrincl = 0;
1875*0Sstevel@tonic-gate 
1876*0Sstevel@tonic-gate 			if (icmp->icmp_family == AF_INET6 &&
1877*0Sstevel@tonic-gate 			    icmp->icmp_proto == IPPROTO_ICMPV6) {
1878*0Sstevel@tonic-gate 				/* Set offset for icmp6_cksum */
1879*0Sstevel@tonic-gate 				icmp->icmp_raw_checksum = 0;
1880*0Sstevel@tonic-gate 				icmp->icmp_checksum_off = 2;
1881*0Sstevel@tonic-gate 			}
1882*0Sstevel@tonic-gate 			if (icmp->icmp_proto == IPPROTO_UDP ||
1883*0Sstevel@tonic-gate 			    icmp->icmp_proto == IPPROTO_TCP ||
1884*0Sstevel@tonic-gate 			    icmp->icmp_proto == IPPROTO_SCTP) {
1885*0Sstevel@tonic-gate 				icmp->icmp_no_tp_cksum = 1;
1886*0Sstevel@tonic-gate 				icmp->icmp_sticky_ipp.ipp_fields |=
1887*0Sstevel@tonic-gate 				    IPPF_NO_CKSUM;
1888*0Sstevel@tonic-gate 			} else {
1889*0Sstevel@tonic-gate 				icmp->icmp_no_tp_cksum = 0;
1890*0Sstevel@tonic-gate 				icmp->icmp_sticky_ipp.ipp_fields &=
1891*0Sstevel@tonic-gate 				    ~IPPF_NO_CKSUM;
1892*0Sstevel@tonic-gate 			}
1893*0Sstevel@tonic-gate 
1894*0Sstevel@tonic-gate 			if (icmp->icmp_filter != NULL &&
1895*0Sstevel@tonic-gate 			    icmp->icmp_proto != IPPROTO_ICMPV6) {
1896*0Sstevel@tonic-gate 				kmem_free(icmp->icmp_filter,
1897*0Sstevel@tonic-gate 				    sizeof (icmp6_filter_t));
1898*0Sstevel@tonic-gate 				icmp->icmp_filter = NULL;
1899*0Sstevel@tonic-gate 			}
1900*0Sstevel@tonic-gate 
1901*0Sstevel@tonic-gate 			/* Rebuild the header template */
1902*0Sstevel@tonic-gate 			error = icmp_build_hdrs(q, icmp);
1903*0Sstevel@tonic-gate 			if (error != 0) {
1904*0Sstevel@tonic-gate 				*outlenp = 0;
1905*0Sstevel@tonic-gate 				return (error);
1906*0Sstevel@tonic-gate 			}
1907*0Sstevel@tonic-gate 
1908*0Sstevel@tonic-gate 			icmp_bind_proto(q);
1909*0Sstevel@tonic-gate 			*outlenp = sizeof (int);
1910*0Sstevel@tonic-gate 			*(int *)outvalp = *i1 & 0xFF;
1911*0Sstevel@tonic-gate 			return (0);
1912*0Sstevel@tonic-gate 		case SO_REUSEADDR:
1913*0Sstevel@tonic-gate 			if (!checkonly)
1914*0Sstevel@tonic-gate 				icmp->icmp_reuseaddr = onoff;
1915*0Sstevel@tonic-gate 			break;
1916*0Sstevel@tonic-gate 
1917*0Sstevel@tonic-gate 		/*
1918*0Sstevel@tonic-gate 		 * The following three items are available here,
1919*0Sstevel@tonic-gate 		 * but are only meaningful to IP.
1920*0Sstevel@tonic-gate 		 */
1921*0Sstevel@tonic-gate 		case SO_DONTROUTE:
1922*0Sstevel@tonic-gate 			if (!checkonly)
1923*0Sstevel@tonic-gate 				icmp->icmp_dontroute = onoff;
1924*0Sstevel@tonic-gate 			break;
1925*0Sstevel@tonic-gate 		case SO_USELOOPBACK:
1926*0Sstevel@tonic-gate 			if (!checkonly)
1927*0Sstevel@tonic-gate 				icmp->icmp_useloopback = onoff;
1928*0Sstevel@tonic-gate 			break;
1929*0Sstevel@tonic-gate 		case SO_BROADCAST:
1930*0Sstevel@tonic-gate 			if (!checkonly)
1931*0Sstevel@tonic-gate 				icmp->icmp_broadcast = onoff;
1932*0Sstevel@tonic-gate 			break;
1933*0Sstevel@tonic-gate 
1934*0Sstevel@tonic-gate 		case SO_SNDBUF:
1935*0Sstevel@tonic-gate 			if (*i1 > icmp_max_buf) {
1936*0Sstevel@tonic-gate 				*outlenp = 0;
1937*0Sstevel@tonic-gate 				return (ENOBUFS);
1938*0Sstevel@tonic-gate 			}
1939*0Sstevel@tonic-gate 			if (!checkonly) {
1940*0Sstevel@tonic-gate 				q->q_hiwat = *i1;
1941*0Sstevel@tonic-gate 				q->q_next->q_hiwat = *i1;
1942*0Sstevel@tonic-gate 			}
1943*0Sstevel@tonic-gate 			break;
1944*0Sstevel@tonic-gate 		case SO_RCVBUF:
1945*0Sstevel@tonic-gate 			if (*i1 > icmp_max_buf) {
1946*0Sstevel@tonic-gate 				*outlenp = 0;
1947*0Sstevel@tonic-gate 				return (ENOBUFS);
1948*0Sstevel@tonic-gate 			}
1949*0Sstevel@tonic-gate 			if (!checkonly) {
1950*0Sstevel@tonic-gate 				RD(q)->q_hiwat = *i1;
1951*0Sstevel@tonic-gate 				(void) mi_set_sth_hiwat(RD(q), *i1);
1952*0Sstevel@tonic-gate 			}
1953*0Sstevel@tonic-gate 			break;
1954*0Sstevel@tonic-gate 		case SO_DGRAM_ERRIND:
1955*0Sstevel@tonic-gate 			if (!checkonly)
1956*0Sstevel@tonic-gate 				icmp->icmp_dgram_errind = onoff;
1957*0Sstevel@tonic-gate 			break;
1958*0Sstevel@tonic-gate 		/*
1959*0Sstevel@tonic-gate 		 * Following three not meaningful for icmp
1960*0Sstevel@tonic-gate 		 * Action is same as "default" so we keep them
1961*0Sstevel@tonic-gate 		 * in comments.
1962*0Sstevel@tonic-gate 		 * case SO_LINGER:
1963*0Sstevel@tonic-gate 		 * case SO_KEEPALIVE:
1964*0Sstevel@tonic-gate 		 * case SO_OOBINLINE:
1965*0Sstevel@tonic-gate 		 */
1966*0Sstevel@tonic-gate 		default:
1967*0Sstevel@tonic-gate 			*outlenp = 0;
1968*0Sstevel@tonic-gate 			return (EINVAL);
1969*0Sstevel@tonic-gate 		}
1970*0Sstevel@tonic-gate 		break;
1971*0Sstevel@tonic-gate 	case IPPROTO_IP:
1972*0Sstevel@tonic-gate 		/*
1973*0Sstevel@tonic-gate 		 * Only allow IPv4 option processing on IPv4 sockets.
1974*0Sstevel@tonic-gate 		 */
1975*0Sstevel@tonic-gate 		if (icmp->icmp_family != AF_INET) {
1976*0Sstevel@tonic-gate 			*outlenp = 0;
1977*0Sstevel@tonic-gate 			return (ENOPROTOOPT);
1978*0Sstevel@tonic-gate 		}
1979*0Sstevel@tonic-gate 		switch (name) {
1980*0Sstevel@tonic-gate 		case IP_OPTIONS:
1981*0Sstevel@tonic-gate 		case T_IP_OPTIONS:
1982*0Sstevel@tonic-gate 			/* Save options for use by IP. */
1983*0Sstevel@tonic-gate 			if (inlen & 0x3) {
1984*0Sstevel@tonic-gate 				*outlenp = 0;
1985*0Sstevel@tonic-gate 				return (EINVAL);
1986*0Sstevel@tonic-gate 			}
1987*0Sstevel@tonic-gate 			if (checkonly)
1988*0Sstevel@tonic-gate 				break;
1989*0Sstevel@tonic-gate 
1990*0Sstevel@tonic-gate 			if (icmp->icmp_ip_snd_options) {
1991*0Sstevel@tonic-gate 				mi_free((char *)icmp->icmp_ip_snd_options);
1992*0Sstevel@tonic-gate 				icmp->icmp_ip_snd_options_len = 0;
1993*0Sstevel@tonic-gate 				icmp->icmp_ip_snd_options = NULL;
1994*0Sstevel@tonic-gate 			}
1995*0Sstevel@tonic-gate 			if (inlen) {
1996*0Sstevel@tonic-gate 				icmp->icmp_ip_snd_options =
1997*0Sstevel@tonic-gate 				    (uchar_t *)mi_alloc(inlen, BPRI_HI);
1998*0Sstevel@tonic-gate 				if (icmp->icmp_ip_snd_options) {
1999*0Sstevel@tonic-gate 					bcopy(invalp,
2000*0Sstevel@tonic-gate 					    icmp->icmp_ip_snd_options, inlen);
2001*0Sstevel@tonic-gate 					icmp->icmp_ip_snd_options_len = inlen;
2002*0Sstevel@tonic-gate 				}
2003*0Sstevel@tonic-gate 			}
2004*0Sstevel@tonic-gate 			icmp->icmp_max_hdr_len = IP_SIMPLE_HDR_LENGTH +
2005*0Sstevel@tonic-gate 			    icmp->icmp_ip_snd_options_len;
2006*0Sstevel@tonic-gate 			(void) mi_set_sth_wroff(RD(q), icmp->icmp_max_hdr_len +
2007*0Sstevel@tonic-gate 						icmp_wroff_extra);
2008*0Sstevel@tonic-gate 			break;
2009*0Sstevel@tonic-gate 		case IP_HDRINCL:
2010*0Sstevel@tonic-gate 			if (!checkonly)
2011*0Sstevel@tonic-gate 				icmp->icmp_hdrincl = onoff;
2012*0Sstevel@tonic-gate 			break;
2013*0Sstevel@tonic-gate 		case IP_TOS:
2014*0Sstevel@tonic-gate 		case T_IP_TOS:
2015*0Sstevel@tonic-gate 			if (!checkonly) {
2016*0Sstevel@tonic-gate 				icmp->icmp_type_of_service = (uint8_t)*i1;
2017*0Sstevel@tonic-gate 			}
2018*0Sstevel@tonic-gate 			break;
2019*0Sstevel@tonic-gate 		case IP_TTL:
2020*0Sstevel@tonic-gate 			if (!checkonly) {
2021*0Sstevel@tonic-gate 				icmp->icmp_ttl = (uint8_t)*i1;
2022*0Sstevel@tonic-gate 			}
2023*0Sstevel@tonic-gate 			break;
2024*0Sstevel@tonic-gate 		case IP_MULTICAST_IF:
2025*0Sstevel@tonic-gate 			/*
2026*0Sstevel@tonic-gate 			 * TODO should check OPTMGMT reply and undo this if
2027*0Sstevel@tonic-gate 			 * there is an error.
2028*0Sstevel@tonic-gate 			 */
2029*0Sstevel@tonic-gate 			if (!checkonly)
2030*0Sstevel@tonic-gate 				icmp->icmp_multicast_if_addr = *i1;
2031*0Sstevel@tonic-gate 			break;
2032*0Sstevel@tonic-gate 		case IP_MULTICAST_TTL:
2033*0Sstevel@tonic-gate 			if (!checkonly)
2034*0Sstevel@tonic-gate 				icmp->icmp_multicast_ttl = *invalp;
2035*0Sstevel@tonic-gate 			break;
2036*0Sstevel@tonic-gate 		case IP_MULTICAST_LOOP:
2037*0Sstevel@tonic-gate 			if (!checkonly) {
2038*0Sstevel@tonic-gate 				icmp->icmp_multicast_loop =
2039*0Sstevel@tonic-gate 				    (*invalp == 0) ? 0 : 1;
2040*0Sstevel@tonic-gate 			}
2041*0Sstevel@tonic-gate 			break;
2042*0Sstevel@tonic-gate 		case IP_BOUND_IF:
2043*0Sstevel@tonic-gate 			if (!checkonly)
2044*0Sstevel@tonic-gate 				icmp->icmp_bound_if = *i1;
2045*0Sstevel@tonic-gate 			break;
2046*0Sstevel@tonic-gate 		case IP_UNSPEC_SRC:
2047*0Sstevel@tonic-gate 			if (!checkonly)
2048*0Sstevel@tonic-gate 				icmp->icmp_unspec_source = onoff;
2049*0Sstevel@tonic-gate 			break;
2050*0Sstevel@tonic-gate 		case IP_XMIT_IF:
2051*0Sstevel@tonic-gate 			if (!checkonly)
2052*0Sstevel@tonic-gate 				icmp->icmp_xmit_if = *i1;
2053*0Sstevel@tonic-gate 			break;
2054*0Sstevel@tonic-gate 		case IP_RECVIF:
2055*0Sstevel@tonic-gate 			if (!checkonly)
2056*0Sstevel@tonic-gate 				icmp->icmp_recvif = onoff;
2057*0Sstevel@tonic-gate 			break;
2058*0Sstevel@tonic-gate 		case IP_ADD_MEMBERSHIP:
2059*0Sstevel@tonic-gate 		case IP_DROP_MEMBERSHIP:
2060*0Sstevel@tonic-gate 		case IP_BLOCK_SOURCE:
2061*0Sstevel@tonic-gate 		case IP_UNBLOCK_SOURCE:
2062*0Sstevel@tonic-gate 		case IP_ADD_SOURCE_MEMBERSHIP:
2063*0Sstevel@tonic-gate 		case IP_DROP_SOURCE_MEMBERSHIP:
2064*0Sstevel@tonic-gate 		case MCAST_JOIN_GROUP:
2065*0Sstevel@tonic-gate 		case MCAST_LEAVE_GROUP:
2066*0Sstevel@tonic-gate 		case MCAST_BLOCK_SOURCE:
2067*0Sstevel@tonic-gate 		case MCAST_UNBLOCK_SOURCE:
2068*0Sstevel@tonic-gate 		case MCAST_JOIN_SOURCE_GROUP:
2069*0Sstevel@tonic-gate 		case MCAST_LEAVE_SOURCE_GROUP:
2070*0Sstevel@tonic-gate 		case MRT_INIT:
2071*0Sstevel@tonic-gate 		case MRT_DONE:
2072*0Sstevel@tonic-gate 		case MRT_ADD_VIF:
2073*0Sstevel@tonic-gate 		case MRT_DEL_VIF:
2074*0Sstevel@tonic-gate 		case MRT_ADD_MFC:
2075*0Sstevel@tonic-gate 		case MRT_DEL_MFC:
2076*0Sstevel@tonic-gate 		case MRT_VERSION:
2077*0Sstevel@tonic-gate 		case MRT_ASSERT:
2078*0Sstevel@tonic-gate 		case IP_SEC_OPT:
2079*0Sstevel@tonic-gate 		case IP_DONTFAILOVER_IF:
2080*0Sstevel@tonic-gate 			/*
2081*0Sstevel@tonic-gate 			 * "soft" error (negative)
2082*0Sstevel@tonic-gate 			 * option not handled at this level
2083*0Sstevel@tonic-gate 			 * Note: Do not modify *outlenp
2084*0Sstevel@tonic-gate 			 */
2085*0Sstevel@tonic-gate 			return (-EINVAL);
2086*0Sstevel@tonic-gate 		default:
2087*0Sstevel@tonic-gate 			*outlenp = 0;
2088*0Sstevel@tonic-gate 			return (EINVAL);
2089*0Sstevel@tonic-gate 		}
2090*0Sstevel@tonic-gate 		break;
2091*0Sstevel@tonic-gate 	case IPPROTO_IPV6: {
2092*0Sstevel@tonic-gate 		ip6_pkt_t		*ipp;
2093*0Sstevel@tonic-gate 		boolean_t		sticky;
2094*0Sstevel@tonic-gate 
2095*0Sstevel@tonic-gate 		if (icmp->icmp_family != AF_INET6) {
2096*0Sstevel@tonic-gate 			*outlenp = 0;
2097*0Sstevel@tonic-gate 			return (ENOPROTOOPT);
2098*0Sstevel@tonic-gate 		}
2099*0Sstevel@tonic-gate 		/*
2100*0Sstevel@tonic-gate 		 * Deal with both sticky options and ancillary data
2101*0Sstevel@tonic-gate 		 */
2102*0Sstevel@tonic-gate 		if (thisdg_attrs == NULL) {
2103*0Sstevel@tonic-gate 			/* sticky options, or none */
2104*0Sstevel@tonic-gate 			ipp = &icmp->icmp_sticky_ipp;
2105*0Sstevel@tonic-gate 			sticky = B_TRUE;
2106*0Sstevel@tonic-gate 		} else {
2107*0Sstevel@tonic-gate 			/* ancillary data */
2108*0Sstevel@tonic-gate 			ipp = (ip6_pkt_t *)thisdg_attrs;
2109*0Sstevel@tonic-gate 			sticky = B_FALSE;
2110*0Sstevel@tonic-gate 		}
2111*0Sstevel@tonic-gate 
2112*0Sstevel@tonic-gate 		switch (name) {
2113*0Sstevel@tonic-gate 		case IPV6_MULTICAST_IF:
2114*0Sstevel@tonic-gate 			if (!checkonly)
2115*0Sstevel@tonic-gate 				icmp->icmp_multicast_if_index = *i1;
2116*0Sstevel@tonic-gate 			break;
2117*0Sstevel@tonic-gate 		case IPV6_UNICAST_HOPS:
2118*0Sstevel@tonic-gate 			/* -1 means use default */
2119*0Sstevel@tonic-gate 			if (*i1 < -1 || *i1 > IPV6_MAX_HOPS) {
2120*0Sstevel@tonic-gate 				*outlenp = 0;
2121*0Sstevel@tonic-gate 				return (EINVAL);
2122*0Sstevel@tonic-gate 			}
2123*0Sstevel@tonic-gate 			if (!checkonly) {
2124*0Sstevel@tonic-gate 				if (*i1 == -1) {
2125*0Sstevel@tonic-gate 					icmp->icmp_ttl = ipp->ipp_hoplimit =
2126*0Sstevel@tonic-gate 					    icmp_ipv6_hoplimit;
2127*0Sstevel@tonic-gate 					ipp->ipp_fields &= ~IPPF_HOPLIMIT;
2128*0Sstevel@tonic-gate 					/* Pass modified value to IP. */
2129*0Sstevel@tonic-gate 					*i1 = ipp->ipp_hoplimit;
2130*0Sstevel@tonic-gate 				} else {
2131*0Sstevel@tonic-gate 					icmp->icmp_ttl = ipp->ipp_hoplimit =
2132*0Sstevel@tonic-gate 					    (uint8_t)*i1;
2133*0Sstevel@tonic-gate 					ipp->ipp_fields |= IPPF_HOPLIMIT;
2134*0Sstevel@tonic-gate 				}
2135*0Sstevel@tonic-gate 				/* Rebuild the header template */
2136*0Sstevel@tonic-gate 				error = icmp_build_hdrs(q, icmp);
2137*0Sstevel@tonic-gate 				if (error != 0) {
2138*0Sstevel@tonic-gate 					*outlenp = 0;
2139*0Sstevel@tonic-gate 					return (error);
2140*0Sstevel@tonic-gate 				}
2141*0Sstevel@tonic-gate 			}
2142*0Sstevel@tonic-gate 			break;
2143*0Sstevel@tonic-gate 		case IPV6_MULTICAST_HOPS:
2144*0Sstevel@tonic-gate 			/* -1 means use default */
2145*0Sstevel@tonic-gate 			if (*i1 < -1 || *i1 > IPV6_MAX_HOPS) {
2146*0Sstevel@tonic-gate 				*outlenp = 0;
2147*0Sstevel@tonic-gate 				return (EINVAL);
2148*0Sstevel@tonic-gate 			}
2149*0Sstevel@tonic-gate 			if (!checkonly) {
2150*0Sstevel@tonic-gate 				if (*i1 == -1) {
2151*0Sstevel@tonic-gate 					icmp->icmp_multicast_ttl =
2152*0Sstevel@tonic-gate 					    ipp->ipp_multi_hoplimit =
2153*0Sstevel@tonic-gate 					    IP_DEFAULT_MULTICAST_TTL;
2154*0Sstevel@tonic-gate 					ipp->ipp_fields &= ~IPPF_MULTI_HOPLIMIT;
2155*0Sstevel@tonic-gate 					/* Pass modified value to IP. */
2156*0Sstevel@tonic-gate 					*i1 = ipp->ipp_multi_hoplimit;
2157*0Sstevel@tonic-gate 				} else {
2158*0Sstevel@tonic-gate 					icmp->icmp_multicast_ttl =
2159*0Sstevel@tonic-gate 					    ipp->ipp_multi_hoplimit =
2160*0Sstevel@tonic-gate 					    (uint8_t)*i1;
2161*0Sstevel@tonic-gate 					ipp->ipp_fields |= IPPF_MULTI_HOPLIMIT;
2162*0Sstevel@tonic-gate 				}
2163*0Sstevel@tonic-gate 				/* Rebuild the header template */
2164*0Sstevel@tonic-gate 				error = icmp_build_hdrs(q, icmp);
2165*0Sstevel@tonic-gate 				if (error != 0) {
2166*0Sstevel@tonic-gate 					*outlenp = 0;
2167*0Sstevel@tonic-gate 					return (error);
2168*0Sstevel@tonic-gate 				}
2169*0Sstevel@tonic-gate 			}
2170*0Sstevel@tonic-gate 			break;
2171*0Sstevel@tonic-gate 		case IPV6_MULTICAST_LOOP:
2172*0Sstevel@tonic-gate 			if (*i1 != 0 && *i1 != 1) {
2173*0Sstevel@tonic-gate 				*outlenp = 0;
2174*0Sstevel@tonic-gate 				return (EINVAL);
2175*0Sstevel@tonic-gate 			}
2176*0Sstevel@tonic-gate 			if (!checkonly)
2177*0Sstevel@tonic-gate 				icmp->icmp_multicast_loop = *i1;
2178*0Sstevel@tonic-gate 			break;
2179*0Sstevel@tonic-gate 		case IPV6_CHECKSUM:
2180*0Sstevel@tonic-gate 			/*
2181*0Sstevel@tonic-gate 			 * Integer offset into the user data of where the
2182*0Sstevel@tonic-gate 			 * checksum is located.
2183*0Sstevel@tonic-gate 			 * Offset of -1 disables option.
2184*0Sstevel@tonic-gate 			 * Does not apply to IPPROTO_ICMPV6.
2185*0Sstevel@tonic-gate 			 */
2186*0Sstevel@tonic-gate 			if (icmp->icmp_proto == IPPROTO_ICMPV6 || !sticky) {
2187*0Sstevel@tonic-gate 				*outlenp = 0;
2188*0Sstevel@tonic-gate 				return (EINVAL);
2189*0Sstevel@tonic-gate 			}
2190*0Sstevel@tonic-gate 			if ((*i1 != -1) && ((*i1 < 0) || (*i1 & 0x1) != 0)) {
2191*0Sstevel@tonic-gate 				/* Negative or not 16 bit aligned offset */
2192*0Sstevel@tonic-gate 				*outlenp = 0;
2193*0Sstevel@tonic-gate 				return (EINVAL);
2194*0Sstevel@tonic-gate 			}
2195*0Sstevel@tonic-gate 			if (checkonly)
2196*0Sstevel@tonic-gate 				break;
2197*0Sstevel@tonic-gate 
2198*0Sstevel@tonic-gate 			if (*i1 == -1) {
2199*0Sstevel@tonic-gate 				icmp->icmp_raw_checksum = 0;
2200*0Sstevel@tonic-gate 				ipp->ipp_fields &= ~IPPF_RAW_CKSUM;
2201*0Sstevel@tonic-gate 			} else {
2202*0Sstevel@tonic-gate 				icmp->icmp_raw_checksum = 1;
2203*0Sstevel@tonic-gate 				icmp->icmp_checksum_off = *i1;
2204*0Sstevel@tonic-gate 				ipp->ipp_fields |= IPPF_RAW_CKSUM;
2205*0Sstevel@tonic-gate 			}
2206*0Sstevel@tonic-gate 			/* Rebuild the header template */
2207*0Sstevel@tonic-gate 			error = icmp_build_hdrs(q, icmp);
2208*0Sstevel@tonic-gate 			if (error != 0) {
2209*0Sstevel@tonic-gate 				*outlenp = 0;
2210*0Sstevel@tonic-gate 				return (error);
2211*0Sstevel@tonic-gate 			}
2212*0Sstevel@tonic-gate 			break;
2213*0Sstevel@tonic-gate 		case IPV6_JOIN_GROUP:
2214*0Sstevel@tonic-gate 		case IPV6_LEAVE_GROUP:
2215*0Sstevel@tonic-gate 		case MCAST_JOIN_GROUP:
2216*0Sstevel@tonic-gate 		case MCAST_LEAVE_GROUP:
2217*0Sstevel@tonic-gate 		case MCAST_BLOCK_SOURCE:
2218*0Sstevel@tonic-gate 		case MCAST_UNBLOCK_SOURCE:
2219*0Sstevel@tonic-gate 		case MCAST_JOIN_SOURCE_GROUP:
2220*0Sstevel@tonic-gate 		case MCAST_LEAVE_SOURCE_GROUP:
2221*0Sstevel@tonic-gate 			/*
2222*0Sstevel@tonic-gate 			 * "soft" error (negative)
2223*0Sstevel@tonic-gate 			 * option not handled at this level
2224*0Sstevel@tonic-gate 			 * Note: Do not modify *outlenp
2225*0Sstevel@tonic-gate 			 */
2226*0Sstevel@tonic-gate 			return (-EINVAL);
2227*0Sstevel@tonic-gate 		case IPV6_BOUND_IF:
2228*0Sstevel@tonic-gate 			if (!checkonly)
2229*0Sstevel@tonic-gate 				icmp->icmp_bound_if = *i1;
2230*0Sstevel@tonic-gate 			break;
2231*0Sstevel@tonic-gate 		case IPV6_UNSPEC_SRC:
2232*0Sstevel@tonic-gate 			if (!checkonly)
2233*0Sstevel@tonic-gate 				icmp->icmp_unspec_source = onoff;
2234*0Sstevel@tonic-gate 			break;
2235*0Sstevel@tonic-gate 		case IPV6_RECVTCLASS:
2236*0Sstevel@tonic-gate 			if (!checkonly)
2237*0Sstevel@tonic-gate 				icmp->icmp_ipv6_recvtclass = onoff;
2238*0Sstevel@tonic-gate 			break;
2239*0Sstevel@tonic-gate 		/*
2240*0Sstevel@tonic-gate 		 * Set boolean switches for ancillary data delivery
2241*0Sstevel@tonic-gate 		 */
2242*0Sstevel@tonic-gate 		case IPV6_RECVPKTINFO:
2243*0Sstevel@tonic-gate 			if (!checkonly)
2244*0Sstevel@tonic-gate 				icmp->icmp_ipv6_recvpktinfo = onoff;
2245*0Sstevel@tonic-gate 			break;
2246*0Sstevel@tonic-gate 		case IPV6_RECVPATHMTU:
2247*0Sstevel@tonic-gate 			if (!checkonly)
2248*0Sstevel@tonic-gate 				icmp->icmp_ipv6_recvpathmtu = onoff;
2249*0Sstevel@tonic-gate 			break;
2250*0Sstevel@tonic-gate 		case IPV6_RECVHOPLIMIT:
2251*0Sstevel@tonic-gate 			if (!checkonly)
2252*0Sstevel@tonic-gate 				icmp->icmp_ipv6_recvhoplimit = onoff;
2253*0Sstevel@tonic-gate 			break;
2254*0Sstevel@tonic-gate 		case IPV6_RECVHOPOPTS:
2255*0Sstevel@tonic-gate 			if (!checkonly)
2256*0Sstevel@tonic-gate 				icmp->icmp_ipv6_recvhopopts = onoff;
2257*0Sstevel@tonic-gate 			break;
2258*0Sstevel@tonic-gate 		case IPV6_RECVDSTOPTS:
2259*0Sstevel@tonic-gate 			if (!checkonly)
2260*0Sstevel@tonic-gate 				icmp->icmp_ipv6_recvdstopts = onoff;
2261*0Sstevel@tonic-gate 			break;
2262*0Sstevel@tonic-gate 		case _OLD_IPV6_RECVDSTOPTS:
2263*0Sstevel@tonic-gate 			if (!checkonly)
2264*0Sstevel@tonic-gate 				icmp->icmp_old_ipv6_recvdstopts = onoff;
2265*0Sstevel@tonic-gate 			break;
2266*0Sstevel@tonic-gate 		case IPV6_RECVRTHDRDSTOPTS:
2267*0Sstevel@tonic-gate 			if (!checkonly)
2268*0Sstevel@tonic-gate 				icmp->icmp_ipv6_recvrtdstopts = onoff;
2269*0Sstevel@tonic-gate 			break;
2270*0Sstevel@tonic-gate 		case IPV6_RECVRTHDR:
2271*0Sstevel@tonic-gate 			if (!checkonly)
2272*0Sstevel@tonic-gate 				icmp->icmp_ipv6_recvrthdr = onoff;
2273*0Sstevel@tonic-gate 			break;
2274*0Sstevel@tonic-gate 		/*
2275*0Sstevel@tonic-gate 		 * Set sticky options or ancillary data.
2276*0Sstevel@tonic-gate 		 * If sticky options, (re)build any extension headers
2277*0Sstevel@tonic-gate 		 * that might be needed as a result.
2278*0Sstevel@tonic-gate 		 */
2279*0Sstevel@tonic-gate 		case IPV6_PKTINFO:
2280*0Sstevel@tonic-gate 			/*
2281*0Sstevel@tonic-gate 			 * The source address and ifindex are verified
2282*0Sstevel@tonic-gate 			 * in ip_opt_set(). For ancillary data the
2283*0Sstevel@tonic-gate 			 * source address is checked in ip_wput_v6.
2284*0Sstevel@tonic-gate 			 */
2285*0Sstevel@tonic-gate 			if (inlen != 0 && inlen != sizeof (struct in6_pktinfo))
2286*0Sstevel@tonic-gate 				return (EINVAL);
2287*0Sstevel@tonic-gate 			if (checkonly)
2288*0Sstevel@tonic-gate 				break;
2289*0Sstevel@tonic-gate 
2290*0Sstevel@tonic-gate 			if (inlen == 0) {
2291*0Sstevel@tonic-gate 				ipp->ipp_fields &= ~(IPPF_IFINDEX|IPPF_ADDR);
2292*0Sstevel@tonic-gate 				ipp->ipp_sticky_ignored |=
2293*0Sstevel@tonic-gate 				    (IPPF_IFINDEX|IPPF_ADDR);
2294*0Sstevel@tonic-gate 			} else {
2295*0Sstevel@tonic-gate 				struct in6_pktinfo *pkti;
2296*0Sstevel@tonic-gate 
2297*0Sstevel@tonic-gate 				pkti = (struct in6_pktinfo *)invalp;
2298*0Sstevel@tonic-gate 				ipp->ipp_ifindex = pkti->ipi6_ifindex;
2299*0Sstevel@tonic-gate 				ipp->ipp_addr = pkti->ipi6_addr;
2300*0Sstevel@tonic-gate 				if (ipp->ipp_ifindex != 0)
2301*0Sstevel@tonic-gate 					ipp->ipp_fields |= IPPF_IFINDEX;
2302*0Sstevel@tonic-gate 				else
2303*0Sstevel@tonic-gate 					ipp->ipp_fields &= ~IPPF_IFINDEX;
2304*0Sstevel@tonic-gate 				if (!IN6_IS_ADDR_UNSPECIFIED(
2305*0Sstevel@tonic-gate 				    &ipp->ipp_addr))
2306*0Sstevel@tonic-gate 					ipp->ipp_fields |= IPPF_ADDR;
2307*0Sstevel@tonic-gate 				else
2308*0Sstevel@tonic-gate 					ipp->ipp_fields &= ~IPPF_ADDR;
2309*0Sstevel@tonic-gate 			}
2310*0Sstevel@tonic-gate 			if (sticky) {
2311*0Sstevel@tonic-gate 				error = icmp_build_hdrs(q, icmp);
2312*0Sstevel@tonic-gate 				if (error != 0)
2313*0Sstevel@tonic-gate 					return (error);
2314*0Sstevel@tonic-gate 			}
2315*0Sstevel@tonic-gate 			break;
2316*0Sstevel@tonic-gate 		case IPV6_HOPLIMIT:
2317*0Sstevel@tonic-gate 			if (inlen != 0 && inlen != sizeof (int))
2318*0Sstevel@tonic-gate 				return (EINVAL);
2319*0Sstevel@tonic-gate 			if (checkonly)
2320*0Sstevel@tonic-gate 				break;
2321*0Sstevel@tonic-gate 
2322*0Sstevel@tonic-gate 			if (inlen == 0) {
2323*0Sstevel@tonic-gate 				ipp->ipp_fields &= ~IPPF_HOPLIMIT;
2324*0Sstevel@tonic-gate 				ipp->ipp_sticky_ignored |= IPPF_HOPLIMIT;
2325*0Sstevel@tonic-gate 			} else {
2326*0Sstevel@tonic-gate 				if (*i1 > 255 || *i1 < -1)
2327*0Sstevel@tonic-gate 					return (EINVAL);
2328*0Sstevel@tonic-gate 				if (*i1 == -1)
2329*0Sstevel@tonic-gate 					ipp->ipp_hoplimit = icmp_ipv6_hoplimit;
2330*0Sstevel@tonic-gate 				else
2331*0Sstevel@tonic-gate 					ipp->ipp_hoplimit = *i1;
2332*0Sstevel@tonic-gate 				ipp->ipp_fields |= IPPF_HOPLIMIT;
2333*0Sstevel@tonic-gate 			}
2334*0Sstevel@tonic-gate 			if (sticky) {
2335*0Sstevel@tonic-gate 				error = icmp_build_hdrs(q, icmp);
2336*0Sstevel@tonic-gate 				if (error != 0)
2337*0Sstevel@tonic-gate 					return (error);
2338*0Sstevel@tonic-gate 			}
2339*0Sstevel@tonic-gate 			break;
2340*0Sstevel@tonic-gate 		case IPV6_TCLASS:
2341*0Sstevel@tonic-gate 			/*
2342*0Sstevel@tonic-gate 			 * IPV6_RECVTCLASS accepts -1 as use kernel default
2343*0Sstevel@tonic-gate 			 * and [0, 255] as the actualy traffic class.
2344*0Sstevel@tonic-gate 			 */
2345*0Sstevel@tonic-gate 			if (inlen != 0 && inlen != sizeof (int))
2346*0Sstevel@tonic-gate 				return (EINVAL);
2347*0Sstevel@tonic-gate 			if (checkonly)
2348*0Sstevel@tonic-gate 				break;
2349*0Sstevel@tonic-gate 
2350*0Sstevel@tonic-gate 			if (inlen == 0) {
2351*0Sstevel@tonic-gate 				ipp->ipp_fields &= ~IPPF_TCLASS;
2352*0Sstevel@tonic-gate 				ipp->ipp_sticky_ignored |= IPPF_TCLASS;
2353*0Sstevel@tonic-gate 			} else {
2354*0Sstevel@tonic-gate 				if (*i1 >= 256 || *i1 < -1)
2355*0Sstevel@tonic-gate 					return (EINVAL);
2356*0Sstevel@tonic-gate 				if (*i1 == -1) {
2357*0Sstevel@tonic-gate 					ipp->ipp_tclass =
2358*0Sstevel@tonic-gate 					    IPV6_FLOW_TCLASS(
2359*0Sstevel@tonic-gate 					    IPV6_DEFAULT_VERS_AND_FLOW);
2360*0Sstevel@tonic-gate 				} else {
2361*0Sstevel@tonic-gate 					ipp->ipp_tclass = *i1;
2362*0Sstevel@tonic-gate 				}
2363*0Sstevel@tonic-gate 				ipp->ipp_fields |= IPPF_TCLASS;
2364*0Sstevel@tonic-gate 			}
2365*0Sstevel@tonic-gate 			if (sticky) {
2366*0Sstevel@tonic-gate 				error = icmp_build_hdrs(q, icmp);
2367*0Sstevel@tonic-gate 				if (error != 0)
2368*0Sstevel@tonic-gate 					return (error);
2369*0Sstevel@tonic-gate 			}
2370*0Sstevel@tonic-gate 			break;
2371*0Sstevel@tonic-gate 		case IPV6_NEXTHOP:
2372*0Sstevel@tonic-gate 			/*
2373*0Sstevel@tonic-gate 			 * IP will verify that the nexthop is reachable
2374*0Sstevel@tonic-gate 			 * and fail for sticky options.
2375*0Sstevel@tonic-gate 			 */
2376*0Sstevel@tonic-gate 			if (inlen != 0 && inlen != sizeof (sin6_t))
2377*0Sstevel@tonic-gate 				return (EINVAL);
2378*0Sstevel@tonic-gate 			if (checkonly)
2379*0Sstevel@tonic-gate 				break;
2380*0Sstevel@tonic-gate 
2381*0Sstevel@tonic-gate 			if (inlen == 0) {
2382*0Sstevel@tonic-gate 				ipp->ipp_fields &= ~IPPF_NEXTHOP;
2383*0Sstevel@tonic-gate 				ipp->ipp_sticky_ignored |= IPPF_NEXTHOP;
2384*0Sstevel@tonic-gate 			} else {
2385*0Sstevel@tonic-gate 				sin6_t *sin6 = (sin6_t *)invalp;
2386*0Sstevel@tonic-gate 
2387*0Sstevel@tonic-gate 				if (sin6->sin6_family != AF_INET6)
2388*0Sstevel@tonic-gate 					return (EAFNOSUPPORT);
2389*0Sstevel@tonic-gate 				if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
2390*0Sstevel@tonic-gate 					return (EADDRNOTAVAIL);
2391*0Sstevel@tonic-gate 				ipp->ipp_nexthop = sin6->sin6_addr;
2392*0Sstevel@tonic-gate 				if (!IN6_IS_ADDR_UNSPECIFIED(
2393*0Sstevel@tonic-gate 				    &ipp->ipp_nexthop))
2394*0Sstevel@tonic-gate 					ipp->ipp_fields |= IPPF_NEXTHOP;
2395*0Sstevel@tonic-gate 				else
2396*0Sstevel@tonic-gate 					ipp->ipp_fields &= ~IPPF_NEXTHOP;
2397*0Sstevel@tonic-gate 			}
2398*0Sstevel@tonic-gate 			if (sticky) {
2399*0Sstevel@tonic-gate 				error = icmp_build_hdrs(q, icmp);
2400*0Sstevel@tonic-gate 				if (error != 0)
2401*0Sstevel@tonic-gate 					return (error);
2402*0Sstevel@tonic-gate 			}
2403*0Sstevel@tonic-gate 			break;
2404*0Sstevel@tonic-gate 		case IPV6_HOPOPTS: {
2405*0Sstevel@tonic-gate 			ip6_hbh_t *hopts = (ip6_hbh_t *)invalp;
2406*0Sstevel@tonic-gate 			/*
2407*0Sstevel@tonic-gate 			 * Sanity checks - minimum size, size a multiple of
2408*0Sstevel@tonic-gate 			 * eight bytes, and matching size passed in.
2409*0Sstevel@tonic-gate 			 */
2410*0Sstevel@tonic-gate 			if (inlen != 0 &&
2411*0Sstevel@tonic-gate 			    inlen != (8 * (hopts->ip6h_len + 1)))
2412*0Sstevel@tonic-gate 				return (EINVAL);
2413*0Sstevel@tonic-gate 
2414*0Sstevel@tonic-gate 			if (checkonly)
2415*0Sstevel@tonic-gate 				break;
2416*0Sstevel@tonic-gate 			if (inlen == 0) {
2417*0Sstevel@tonic-gate 				if (sticky &&
2418*0Sstevel@tonic-gate 				    (ipp->ipp_fields & IPPF_HOPOPTS) != 0) {
2419*0Sstevel@tonic-gate 					kmem_free(ipp->ipp_hopopts,
2420*0Sstevel@tonic-gate 					    ipp->ipp_hopoptslen);
2421*0Sstevel@tonic-gate 					ipp->ipp_hopopts = NULL;
2422*0Sstevel@tonic-gate 					ipp->ipp_hopoptslen = 0;
2423*0Sstevel@tonic-gate 				}
2424*0Sstevel@tonic-gate 				ipp->ipp_fields &= ~IPPF_HOPOPTS;
2425*0Sstevel@tonic-gate 				ipp->ipp_sticky_ignored |= IPPF_HOPOPTS;
2426*0Sstevel@tonic-gate 			} else {
2427*0Sstevel@tonic-gate 				error = icmp_pkt_set(invalp, inlen, sticky,
2428*0Sstevel@tonic-gate 				    (uchar_t **)&ipp->ipp_hopopts,
2429*0Sstevel@tonic-gate 				    &ipp->ipp_hopoptslen);
2430*0Sstevel@tonic-gate 				if (error != 0)
2431*0Sstevel@tonic-gate 					return (error);
2432*0Sstevel@tonic-gate 				ipp->ipp_fields |= IPPF_HOPOPTS;
2433*0Sstevel@tonic-gate 			}
2434*0Sstevel@tonic-gate 			if (sticky) {
2435*0Sstevel@tonic-gate 				error = icmp_build_hdrs(q, icmp);
2436*0Sstevel@tonic-gate 				if (error != 0)
2437*0Sstevel@tonic-gate 					return (error);
2438*0Sstevel@tonic-gate 			}
2439*0Sstevel@tonic-gate 			break;
2440*0Sstevel@tonic-gate 		}
2441*0Sstevel@tonic-gate 		case IPV6_RTHDRDSTOPTS: {
2442*0Sstevel@tonic-gate 			ip6_dest_t *dopts = (ip6_dest_t *)invalp;
2443*0Sstevel@tonic-gate 
2444*0Sstevel@tonic-gate 			/*
2445*0Sstevel@tonic-gate 			 * Sanity checks - minimum size, size a multiple of
2446*0Sstevel@tonic-gate 			 * eight bytes, and matching size passed in.
2447*0Sstevel@tonic-gate 			 */
2448*0Sstevel@tonic-gate 			if (inlen != 0 &&
2449*0Sstevel@tonic-gate 			    inlen != (8 * (dopts->ip6d_len + 1)))
2450*0Sstevel@tonic-gate 				return (EINVAL);
2451*0Sstevel@tonic-gate 
2452*0Sstevel@tonic-gate 			if (checkonly)
2453*0Sstevel@tonic-gate 				break;
2454*0Sstevel@tonic-gate 
2455*0Sstevel@tonic-gate 			if (inlen == 0) {
2456*0Sstevel@tonic-gate 				if (sticky &&
2457*0Sstevel@tonic-gate 				    (ipp->ipp_fields & IPPF_RTDSTOPTS) != 0) {
2458*0Sstevel@tonic-gate 					kmem_free(ipp->ipp_rtdstopts,
2459*0Sstevel@tonic-gate 					    ipp->ipp_rtdstoptslen);
2460*0Sstevel@tonic-gate 					ipp->ipp_rtdstopts = NULL;
2461*0Sstevel@tonic-gate 					ipp->ipp_rtdstoptslen = 0;
2462*0Sstevel@tonic-gate 				}
2463*0Sstevel@tonic-gate 				ipp->ipp_fields &= ~IPPF_RTDSTOPTS;
2464*0Sstevel@tonic-gate 				ipp->ipp_sticky_ignored |= IPPF_RTDSTOPTS;
2465*0Sstevel@tonic-gate 			} else {
2466*0Sstevel@tonic-gate 				error = icmp_pkt_set(invalp, inlen, sticky,
2467*0Sstevel@tonic-gate 				    (uchar_t **)&ipp->ipp_rtdstopts,
2468*0Sstevel@tonic-gate 				    &ipp->ipp_rtdstoptslen);
2469*0Sstevel@tonic-gate 				if (error != 0)
2470*0Sstevel@tonic-gate 					return (error);
2471*0Sstevel@tonic-gate 				ipp->ipp_fields |= IPPF_RTDSTOPTS;
2472*0Sstevel@tonic-gate 			}
2473*0Sstevel@tonic-gate 			if (sticky) {
2474*0Sstevel@tonic-gate 				error = icmp_build_hdrs(q, icmp);
2475*0Sstevel@tonic-gate 				if (error != 0)
2476*0Sstevel@tonic-gate 					return (error);
2477*0Sstevel@tonic-gate 			}
2478*0Sstevel@tonic-gate 			break;
2479*0Sstevel@tonic-gate 		}
2480*0Sstevel@tonic-gate 		case IPV6_DSTOPTS: {
2481*0Sstevel@tonic-gate 			ip6_dest_t *dopts = (ip6_dest_t *)invalp;
2482*0Sstevel@tonic-gate 
2483*0Sstevel@tonic-gate 			/*
2484*0Sstevel@tonic-gate 			 * Sanity checks - minimum size, size a multiple of
2485*0Sstevel@tonic-gate 			 * eight bytes, and matching size passed in.
2486*0Sstevel@tonic-gate 			 */
2487*0Sstevel@tonic-gate 			if (inlen != 0 &&
2488*0Sstevel@tonic-gate 			    inlen != (8 * (dopts->ip6d_len + 1)))
2489*0Sstevel@tonic-gate 				return (EINVAL);
2490*0Sstevel@tonic-gate 
2491*0Sstevel@tonic-gate 			if (checkonly)
2492*0Sstevel@tonic-gate 				break;
2493*0Sstevel@tonic-gate 
2494*0Sstevel@tonic-gate 			if (inlen == 0) {
2495*0Sstevel@tonic-gate 				if (sticky &&
2496*0Sstevel@tonic-gate 				    (ipp->ipp_fields & IPPF_DSTOPTS) != 0) {
2497*0Sstevel@tonic-gate 					kmem_free(ipp->ipp_dstopts,
2498*0Sstevel@tonic-gate 					    ipp->ipp_dstoptslen);
2499*0Sstevel@tonic-gate 					ipp->ipp_dstopts = NULL;
2500*0Sstevel@tonic-gate 					ipp->ipp_dstoptslen = 0;
2501*0Sstevel@tonic-gate 				}
2502*0Sstevel@tonic-gate 				ipp->ipp_fields &= ~IPPF_DSTOPTS;
2503*0Sstevel@tonic-gate 				ipp->ipp_sticky_ignored |= IPPF_DSTOPTS;
2504*0Sstevel@tonic-gate 			} else {
2505*0Sstevel@tonic-gate 				error = icmp_pkt_set(invalp, inlen, sticky,
2506*0Sstevel@tonic-gate 				    (uchar_t **)&ipp->ipp_dstopts,
2507*0Sstevel@tonic-gate 				    &ipp->ipp_dstoptslen);
2508*0Sstevel@tonic-gate 				if (error != 0)
2509*0Sstevel@tonic-gate 					return (error);
2510*0Sstevel@tonic-gate 				ipp->ipp_fields |= IPPF_DSTOPTS;
2511*0Sstevel@tonic-gate 			}
2512*0Sstevel@tonic-gate 			if (sticky) {
2513*0Sstevel@tonic-gate 				error = icmp_build_hdrs(q, icmp);
2514*0Sstevel@tonic-gate 				if (error != 0)
2515*0Sstevel@tonic-gate 					return (error);
2516*0Sstevel@tonic-gate 			}
2517*0Sstevel@tonic-gate 			break;
2518*0Sstevel@tonic-gate 		}
2519*0Sstevel@tonic-gate 		case IPV6_RTHDR: {
2520*0Sstevel@tonic-gate 			ip6_rthdr_t *rt = (ip6_rthdr_t *)invalp;
2521*0Sstevel@tonic-gate 
2522*0Sstevel@tonic-gate 			/*
2523*0Sstevel@tonic-gate 			 * Sanity checks - minimum size, size a multiple of
2524*0Sstevel@tonic-gate 			 * eight bytes, and matching size passed in.
2525*0Sstevel@tonic-gate 			 */
2526*0Sstevel@tonic-gate 			if (inlen != 0 &&
2527*0Sstevel@tonic-gate 			    inlen != (8 * (rt->ip6r_len + 1)))
2528*0Sstevel@tonic-gate 				return (EINVAL);
2529*0Sstevel@tonic-gate 
2530*0Sstevel@tonic-gate 			if (checkonly)
2531*0Sstevel@tonic-gate 				break;
2532*0Sstevel@tonic-gate 
2533*0Sstevel@tonic-gate 			if (inlen == 0) {
2534*0Sstevel@tonic-gate 				if (sticky &&
2535*0Sstevel@tonic-gate 				    (ipp->ipp_fields & IPPF_RTHDR) != 0) {
2536*0Sstevel@tonic-gate 					kmem_free(ipp->ipp_rthdr,
2537*0Sstevel@tonic-gate 					    ipp->ipp_rthdrlen);
2538*0Sstevel@tonic-gate 					ipp->ipp_rthdr = NULL;
2539*0Sstevel@tonic-gate 					ipp->ipp_rthdrlen = 0;
2540*0Sstevel@tonic-gate 				}
2541*0Sstevel@tonic-gate 				ipp->ipp_fields &= ~IPPF_RTHDR;
2542*0Sstevel@tonic-gate 				ipp->ipp_sticky_ignored |= IPPF_RTHDR;
2543*0Sstevel@tonic-gate 			} else {
2544*0Sstevel@tonic-gate 				error = icmp_pkt_set(invalp, inlen, sticky,
2545*0Sstevel@tonic-gate 				    (uchar_t **)&ipp->ipp_rthdr,
2546*0Sstevel@tonic-gate 				    &ipp->ipp_rthdrlen);
2547*0Sstevel@tonic-gate 				if (error != 0)
2548*0Sstevel@tonic-gate 					return (error);
2549*0Sstevel@tonic-gate 				ipp->ipp_fields |= IPPF_RTHDR;
2550*0Sstevel@tonic-gate 			}
2551*0Sstevel@tonic-gate 			if (sticky) {
2552*0Sstevel@tonic-gate 				error = icmp_build_hdrs(q, icmp);
2553*0Sstevel@tonic-gate 				if (error != 0)
2554*0Sstevel@tonic-gate 					return (error);
2555*0Sstevel@tonic-gate 			}
2556*0Sstevel@tonic-gate 			break;
2557*0Sstevel@tonic-gate 		}
2558*0Sstevel@tonic-gate 
2559*0Sstevel@tonic-gate 		case IPV6_DONTFRAG:
2560*0Sstevel@tonic-gate 			if (checkonly)
2561*0Sstevel@tonic-gate 				break;
2562*0Sstevel@tonic-gate 
2563*0Sstevel@tonic-gate 			if (onoff) {
2564*0Sstevel@tonic-gate 				ipp->ipp_fields |= IPPF_DONTFRAG;
2565*0Sstevel@tonic-gate 			} else {
2566*0Sstevel@tonic-gate 				ipp->ipp_fields &= ~IPPF_DONTFRAG;
2567*0Sstevel@tonic-gate 			}
2568*0Sstevel@tonic-gate 			break;
2569*0Sstevel@tonic-gate 
2570*0Sstevel@tonic-gate 		case IPV6_USE_MIN_MTU:
2571*0Sstevel@tonic-gate 			if (inlen != sizeof (int))
2572*0Sstevel@tonic-gate 				return (EINVAL);
2573*0Sstevel@tonic-gate 
2574*0Sstevel@tonic-gate 			if (*i1 < -1 || *i1 > 1)
2575*0Sstevel@tonic-gate 				return (EINVAL);
2576*0Sstevel@tonic-gate 
2577*0Sstevel@tonic-gate 			if (checkonly)
2578*0Sstevel@tonic-gate 				break;
2579*0Sstevel@tonic-gate 
2580*0Sstevel@tonic-gate 			ipp->ipp_fields |= IPPF_USE_MIN_MTU;
2581*0Sstevel@tonic-gate 			ipp->ipp_use_min_mtu = *i1;
2582*0Sstevel@tonic-gate 			break;
2583*0Sstevel@tonic-gate 
2584*0Sstevel@tonic-gate 		/*
2585*0Sstevel@tonic-gate 		 * This option can't be set.  Its only returned via
2586*0Sstevel@tonic-gate 		 * getsockopt() or ancillary data.
2587*0Sstevel@tonic-gate 		 */
2588*0Sstevel@tonic-gate 		case IPV6_PATHMTU:
2589*0Sstevel@tonic-gate 			return (EINVAL);
2590*0Sstevel@tonic-gate 
2591*0Sstevel@tonic-gate 		case IPV6_BOUND_PIF:
2592*0Sstevel@tonic-gate 		case IPV6_SEC_OPT:
2593*0Sstevel@tonic-gate 		case IPV6_DONTFAILOVER_IF:
2594*0Sstevel@tonic-gate 		case IPV6_SRC_PREFERENCES:
2595*0Sstevel@tonic-gate 		case IPV6_V6ONLY:
2596*0Sstevel@tonic-gate 			/* Handled at IP level */
2597*0Sstevel@tonic-gate 			return (-EINVAL);
2598*0Sstevel@tonic-gate 		default:
2599*0Sstevel@tonic-gate 			*outlenp = 0;
2600*0Sstevel@tonic-gate 			return (EINVAL);
2601*0Sstevel@tonic-gate 		}
2602*0Sstevel@tonic-gate 		break;
2603*0Sstevel@tonic-gate 	}		/* end IPPROTO_IPV6 */
2604*0Sstevel@tonic-gate 
2605*0Sstevel@tonic-gate 	case IPPROTO_ICMPV6:
2606*0Sstevel@tonic-gate 		/*
2607*0Sstevel@tonic-gate 		 * Only allow IPv6 option processing on IPv6 sockets.
2608*0Sstevel@tonic-gate 		 */
2609*0Sstevel@tonic-gate 		if (icmp->icmp_family != AF_INET6) {
2610*0Sstevel@tonic-gate 			*outlenp = 0;
2611*0Sstevel@tonic-gate 			return (ENOPROTOOPT);
2612*0Sstevel@tonic-gate 		}
2613*0Sstevel@tonic-gate 		if (icmp->icmp_proto != IPPROTO_ICMPV6) {
2614*0Sstevel@tonic-gate 			*outlenp = 0;
2615*0Sstevel@tonic-gate 			return (ENOPROTOOPT);
2616*0Sstevel@tonic-gate 		}
2617*0Sstevel@tonic-gate 		switch (name) {
2618*0Sstevel@tonic-gate 		case ICMP6_FILTER:
2619*0Sstevel@tonic-gate 			if (!checkonly) {
2620*0Sstevel@tonic-gate 				if ((inlen != 0) &&
2621*0Sstevel@tonic-gate 				    (inlen != sizeof (icmp6_filter_t)))
2622*0Sstevel@tonic-gate 					return (EINVAL);
2623*0Sstevel@tonic-gate 
2624*0Sstevel@tonic-gate 				if (inlen == 0) {
2625*0Sstevel@tonic-gate 					if (icmp->icmp_filter != NULL) {
2626*0Sstevel@tonic-gate 						kmem_free(icmp->icmp_filter,
2627*0Sstevel@tonic-gate 						    sizeof (icmp6_filter_t));
2628*0Sstevel@tonic-gate 						icmp->icmp_filter = NULL;
2629*0Sstevel@tonic-gate 					}
2630*0Sstevel@tonic-gate 				} else {
2631*0Sstevel@tonic-gate 					if (icmp->icmp_filter == NULL) {
2632*0Sstevel@tonic-gate 						icmp->icmp_filter = kmem_alloc(
2633*0Sstevel@tonic-gate 						    sizeof (icmp6_filter_t),
2634*0Sstevel@tonic-gate 						    KM_NOSLEEP);
2635*0Sstevel@tonic-gate 						if (icmp->icmp_filter == NULL) {
2636*0Sstevel@tonic-gate 							*outlenp = 0;
2637*0Sstevel@tonic-gate 							return (ENOBUFS);
2638*0Sstevel@tonic-gate 						}
2639*0Sstevel@tonic-gate 					}
2640*0Sstevel@tonic-gate 					(void) bcopy(invalp, icmp->icmp_filter,
2641*0Sstevel@tonic-gate 					    inlen);
2642*0Sstevel@tonic-gate 				}
2643*0Sstevel@tonic-gate 			}
2644*0Sstevel@tonic-gate 			break;
2645*0Sstevel@tonic-gate 
2646*0Sstevel@tonic-gate 		default:
2647*0Sstevel@tonic-gate 			*outlenp = 0;
2648*0Sstevel@tonic-gate 			return (EINVAL);
2649*0Sstevel@tonic-gate 		}
2650*0Sstevel@tonic-gate 		break;
2651*0Sstevel@tonic-gate 	default:
2652*0Sstevel@tonic-gate 		*outlenp = 0;
2653*0Sstevel@tonic-gate 		return (EINVAL);
2654*0Sstevel@tonic-gate 	}
2655*0Sstevel@tonic-gate 	/*
2656*0Sstevel@tonic-gate 	 * Common case of OK return with outval same as inval.
2657*0Sstevel@tonic-gate 	 */
2658*0Sstevel@tonic-gate 	if (invalp != outvalp) {
2659*0Sstevel@tonic-gate 		/* don't trust bcopy for identical src/dst */
2660*0Sstevel@tonic-gate 		(void) bcopy(invalp, outvalp, inlen);
2661*0Sstevel@tonic-gate 	}
2662*0Sstevel@tonic-gate 	*outlenp = inlen;
2663*0Sstevel@tonic-gate 	return (0);
2664*0Sstevel@tonic-gate }
2665*0Sstevel@tonic-gate 
2666*0Sstevel@tonic-gate /*
2667*0Sstevel@tonic-gate  * Update icmp_sticky_hdrs based on icmp_sticky_ipp, icmp_v6src, icmp_ttl,
2668*0Sstevel@tonic-gate  * icmp_proto, icmp_raw_checksum and icmp_no_tp_cksum.
2669*0Sstevel@tonic-gate  * The headers include ip6i_t (if needed), ip6_t, and any sticky extension
2670*0Sstevel@tonic-gate  * headers.
2671*0Sstevel@tonic-gate  * Returns failure if can't allocate memory.
2672*0Sstevel@tonic-gate  */
2673*0Sstevel@tonic-gate static int
2674*0Sstevel@tonic-gate icmp_build_hdrs(queue_t *q, icmp_t *icmp)
2675*0Sstevel@tonic-gate {
2676*0Sstevel@tonic-gate 	uchar_t	*hdrs;
2677*0Sstevel@tonic-gate 	uint_t	hdrs_len;
2678*0Sstevel@tonic-gate 	ip6_t	*ip6h;
2679*0Sstevel@tonic-gate 	ip6i_t	*ip6i;
2680*0Sstevel@tonic-gate 	ip6_pkt_t *ipp = &icmp->icmp_sticky_ipp;
2681*0Sstevel@tonic-gate 
2682*0Sstevel@tonic-gate 	hdrs_len = ip_total_hdrs_len_v6(ipp);
2683*0Sstevel@tonic-gate 	ASSERT(hdrs_len != 0);
2684*0Sstevel@tonic-gate 	if (hdrs_len != icmp->icmp_sticky_hdrs_len) {
2685*0Sstevel@tonic-gate 		/* Need to reallocate */
2686*0Sstevel@tonic-gate 		if (hdrs_len != 0) {
2687*0Sstevel@tonic-gate 			hdrs = kmem_alloc(hdrs_len, KM_NOSLEEP);
2688*0Sstevel@tonic-gate 			if (hdrs == NULL)
2689*0Sstevel@tonic-gate 				return (ENOMEM);
2690*0Sstevel@tonic-gate 		} else {
2691*0Sstevel@tonic-gate 			hdrs = NULL;
2692*0Sstevel@tonic-gate 		}
2693*0Sstevel@tonic-gate 		if (icmp->icmp_sticky_hdrs_len != 0) {
2694*0Sstevel@tonic-gate 			kmem_free(icmp->icmp_sticky_hdrs,
2695*0Sstevel@tonic-gate 			    icmp->icmp_sticky_hdrs_len);
2696*0Sstevel@tonic-gate 		}
2697*0Sstevel@tonic-gate 		icmp->icmp_sticky_hdrs = hdrs;
2698*0Sstevel@tonic-gate 		icmp->icmp_sticky_hdrs_len = hdrs_len;
2699*0Sstevel@tonic-gate 	}
2700*0Sstevel@tonic-gate 	ip_build_hdrs_v6(icmp->icmp_sticky_hdrs,
2701*0Sstevel@tonic-gate 	    icmp->icmp_sticky_hdrs_len, ipp, icmp->icmp_proto);
2702*0Sstevel@tonic-gate 
2703*0Sstevel@tonic-gate 	/* Set header fields not in ipp */
2704*0Sstevel@tonic-gate 	if (ipp->ipp_fields & IPPF_HAS_IP6I) {
2705*0Sstevel@tonic-gate 		ip6i = (ip6i_t *)icmp->icmp_sticky_hdrs;
2706*0Sstevel@tonic-gate 		ip6h = (ip6_t *)&ip6i[1];
2707*0Sstevel@tonic-gate 
2708*0Sstevel@tonic-gate 		if (ipp->ipp_fields & IPPF_RAW_CKSUM) {
2709*0Sstevel@tonic-gate 			ip6i->ip6i_flags |= IP6I_RAW_CHECKSUM;
2710*0Sstevel@tonic-gate 			ip6i->ip6i_checksum_off = icmp->icmp_checksum_off;
2711*0Sstevel@tonic-gate 		}
2712*0Sstevel@tonic-gate 		if (ipp->ipp_fields & IPPF_NO_CKSUM) {
2713*0Sstevel@tonic-gate 			ip6i->ip6i_flags |= IP6I_NO_ULP_CKSUM;
2714*0Sstevel@tonic-gate 		}
2715*0Sstevel@tonic-gate 	} else {
2716*0Sstevel@tonic-gate 		ip6h = (ip6_t *)icmp->icmp_sticky_hdrs;
2717*0Sstevel@tonic-gate 	}
2718*0Sstevel@tonic-gate 
2719*0Sstevel@tonic-gate 	if (!(ipp->ipp_fields & IPPF_ADDR))
2720*0Sstevel@tonic-gate 		ip6h->ip6_src = icmp->icmp_v6src;
2721*0Sstevel@tonic-gate 
2722*0Sstevel@tonic-gate 	/*
2723*0Sstevel@tonic-gate 	 * If IPV6_HOPLIMIT was set in ipp, use that value.
2724*0Sstevel@tonic-gate 	 * For sticky options, if it does not exist use
2725*0Sstevel@tonic-gate 	 * the value in the icmp structure.
2726*0Sstevel@tonic-gate 	 * All this as per RFC 2922.
2727*0Sstevel@tonic-gate 	 */
2728*0Sstevel@tonic-gate 	if (!(ipp->ipp_fields & IPPF_HOPLIMIT))
2729*0Sstevel@tonic-gate 		ip6h->ip6_hops = icmp->icmp_ttl;
2730*0Sstevel@tonic-gate 
2731*0Sstevel@tonic-gate 	/* Try to get everything in a single mblk */
2732*0Sstevel@tonic-gate 	if (hdrs_len > icmp->icmp_max_hdr_len) {
2733*0Sstevel@tonic-gate 		icmp->icmp_max_hdr_len = hdrs_len;
2734*0Sstevel@tonic-gate 		(void) mi_set_sth_wroff(RD(q), icmp->icmp_max_hdr_len +
2735*0Sstevel@tonic-gate 		    icmp_wroff_extra);
2736*0Sstevel@tonic-gate 	}
2737*0Sstevel@tonic-gate 	return (0);
2738*0Sstevel@tonic-gate }
2739*0Sstevel@tonic-gate 
2740*0Sstevel@tonic-gate /*
2741*0Sstevel@tonic-gate  * Set optbuf and optlen for the option.
2742*0Sstevel@tonic-gate  * If sticky is set allocate memory (if not already present).
2743*0Sstevel@tonic-gate  * Otherwise just point optbuf and optlen at invalp and inlen.
2744*0Sstevel@tonic-gate  * Returns failure if memory can not be allocated.
2745*0Sstevel@tonic-gate  */
2746*0Sstevel@tonic-gate static int
2747*0Sstevel@tonic-gate icmp_pkt_set(uchar_t *invalp, uint_t inlen, boolean_t sticky,
2748*0Sstevel@tonic-gate     uchar_t **optbufp, uint_t *optlenp)
2749*0Sstevel@tonic-gate {
2750*0Sstevel@tonic-gate 	uchar_t *optbuf;
2751*0Sstevel@tonic-gate 
2752*0Sstevel@tonic-gate 	if (!sticky) {
2753*0Sstevel@tonic-gate 		*optbufp = invalp;
2754*0Sstevel@tonic-gate 		*optlenp = inlen;
2755*0Sstevel@tonic-gate 		return (0);
2756*0Sstevel@tonic-gate 	}
2757*0Sstevel@tonic-gate 	if (inlen == *optlenp) {
2758*0Sstevel@tonic-gate 		/* Unchanged length - no need to realocate */
2759*0Sstevel@tonic-gate 		bcopy(invalp, *optbufp, inlen);
2760*0Sstevel@tonic-gate 		return (0);
2761*0Sstevel@tonic-gate 	}
2762*0Sstevel@tonic-gate 	if (inlen != 0) {
2763*0Sstevel@tonic-gate 		/* Allocate new buffer before free */
2764*0Sstevel@tonic-gate 		optbuf = kmem_alloc(inlen, KM_NOSLEEP);
2765*0Sstevel@tonic-gate 		if (optbuf == NULL)
2766*0Sstevel@tonic-gate 			return (ENOMEM);
2767*0Sstevel@tonic-gate 	} else {
2768*0Sstevel@tonic-gate 		optbuf = NULL;
2769*0Sstevel@tonic-gate 	}
2770*0Sstevel@tonic-gate 	/* Free old buffer */
2771*0Sstevel@tonic-gate 	if (*optlenp != 0)
2772*0Sstevel@tonic-gate 		kmem_free(*optbufp, *optlenp);
2773*0Sstevel@tonic-gate 
2774*0Sstevel@tonic-gate 	bcopy(invalp, optbuf, inlen);
2775*0Sstevel@tonic-gate 	*optbufp = optbuf;
2776*0Sstevel@tonic-gate 	*optlenp = inlen;
2777*0Sstevel@tonic-gate 	return (0);
2778*0Sstevel@tonic-gate }
2779*0Sstevel@tonic-gate 
2780*0Sstevel@tonic-gate /*
2781*0Sstevel@tonic-gate  * This routine retrieves the value of an ND variable in a icmpparam_t
2782*0Sstevel@tonic-gate  * structure.  It is called through nd_getset when a user reads the
2783*0Sstevel@tonic-gate  * variable.
2784*0Sstevel@tonic-gate  */
2785*0Sstevel@tonic-gate /* ARGSUSED */
2786*0Sstevel@tonic-gate static int
2787*0Sstevel@tonic-gate icmp_param_get(queue_t *q, mblk_t *mp, caddr_t cp, cred_t *cr)
2788*0Sstevel@tonic-gate {
2789*0Sstevel@tonic-gate 	icmpparam_t	*icmppa = (icmpparam_t *)cp;
2790*0Sstevel@tonic-gate 
2791*0Sstevel@tonic-gate 	(void) mi_mpprintf(mp, "%d", icmppa->icmp_param_value);
2792*0Sstevel@tonic-gate 	return (0);
2793*0Sstevel@tonic-gate }
2794*0Sstevel@tonic-gate 
2795*0Sstevel@tonic-gate /*
2796*0Sstevel@tonic-gate  * Walk through the param array specified registering each element with the
2797*0Sstevel@tonic-gate  * named dispatch (ND) handler.
2798*0Sstevel@tonic-gate  */
2799*0Sstevel@tonic-gate static boolean_t
2800*0Sstevel@tonic-gate icmp_param_register(icmpparam_t *icmppa, int cnt)
2801*0Sstevel@tonic-gate {
2802*0Sstevel@tonic-gate 	for (; cnt-- > 0; icmppa++) {
2803*0Sstevel@tonic-gate 		if (icmppa->icmp_param_name && icmppa->icmp_param_name[0]) {
2804*0Sstevel@tonic-gate 			if (!nd_load(&icmp_g_nd, icmppa->icmp_param_name,
2805*0Sstevel@tonic-gate 			    icmp_param_get, icmp_param_set,
2806*0Sstevel@tonic-gate 			    (caddr_t)icmppa)) {
2807*0Sstevel@tonic-gate 				nd_free(&icmp_g_nd);
2808*0Sstevel@tonic-gate 				return (B_FALSE);
2809*0Sstevel@tonic-gate 			}
2810*0Sstevel@tonic-gate 		}
2811*0Sstevel@tonic-gate 	}
2812*0Sstevel@tonic-gate 	if (!nd_load(&icmp_g_nd, "icmp_status", icmp_status_report, NULL,
2813*0Sstevel@tonic-gate 	    NULL)) {
2814*0Sstevel@tonic-gate 		nd_free(&icmp_g_nd);
2815*0Sstevel@tonic-gate 		return (B_FALSE);
2816*0Sstevel@tonic-gate 	}
2817*0Sstevel@tonic-gate 	return (B_TRUE);
2818*0Sstevel@tonic-gate }
2819*0Sstevel@tonic-gate 
2820*0Sstevel@tonic-gate /* This routine sets an ND variable in a icmpparam_t structure. */
2821*0Sstevel@tonic-gate /* ARGSUSED */
2822*0Sstevel@tonic-gate static int
2823*0Sstevel@tonic-gate icmp_param_set(queue_t *q, mblk_t *mp, char *value, caddr_t cp, cred_t *cr)
2824*0Sstevel@tonic-gate {
2825*0Sstevel@tonic-gate 	long		new_value;
2826*0Sstevel@tonic-gate 	icmpparam_t	*icmppa = (icmpparam_t *)cp;
2827*0Sstevel@tonic-gate 
2828*0Sstevel@tonic-gate 	/*
2829*0Sstevel@tonic-gate 	 * Fail the request if the new value does not lie within the
2830*0Sstevel@tonic-gate 	 * required bounds.
2831*0Sstevel@tonic-gate 	 */
2832*0Sstevel@tonic-gate 	if (ddi_strtol(value, NULL, 10, &new_value) != 0 ||
2833*0Sstevel@tonic-gate 	    new_value < icmppa->icmp_param_min ||
2834*0Sstevel@tonic-gate 	    new_value > icmppa->icmp_param_max) {
2835*0Sstevel@tonic-gate 		return (EINVAL);
2836*0Sstevel@tonic-gate 	}
2837*0Sstevel@tonic-gate 	/* Set the new value */
2838*0Sstevel@tonic-gate 	icmppa->icmp_param_value = new_value;
2839*0Sstevel@tonic-gate 	return (0);
2840*0Sstevel@tonic-gate }
2841*0Sstevel@tonic-gate 
2842*0Sstevel@tonic-gate static void
2843*0Sstevel@tonic-gate icmp_rput(queue_t *q, mblk_t *mp)
2844*0Sstevel@tonic-gate {
2845*0Sstevel@tonic-gate 	struct T_unitdata_ind	*tudi;
2846*0Sstevel@tonic-gate 	uchar_t			*rptr;
2847*0Sstevel@tonic-gate 	struct T_error_ack	*tea;
2848*0Sstevel@tonic-gate 	icmp_t			*icmp;
2849*0Sstevel@tonic-gate 	sin_t			*sin;
2850*0Sstevel@tonic-gate 	sin6_t			*sin6;
2851*0Sstevel@tonic-gate 	ip6_t			*ip6h;
2852*0Sstevel@tonic-gate 	ip6i_t			*ip6i;
2853*0Sstevel@tonic-gate 	mblk_t			*mp1;
2854*0Sstevel@tonic-gate 	int			hdr_len;
2855*0Sstevel@tonic-gate 	ipha_t			*ipha;
2856*0Sstevel@tonic-gate 	int			udi_size;	/* Size of T_unitdata_ind */
2857*0Sstevel@tonic-gate 	uint_t			ipvers;
2858*0Sstevel@tonic-gate 	ip6_pkt_t		ipp;
2859*0Sstevel@tonic-gate 	uint8_t			nexthdr;
2860*0Sstevel@tonic-gate 	boolean_t		recvif = B_FALSE;
2861*0Sstevel@tonic-gate 	in_pktinfo_t		*pinfo;
2862*0Sstevel@tonic-gate 	mblk_t			*options_mp = NULL;
2863*0Sstevel@tonic-gate 	uint_t			icmp_opt = 0;
2864*0Sstevel@tonic-gate 	boolean_t		icmp_ipv6_recvhoplimit = B_FALSE;
2865*0Sstevel@tonic-gate 
2866*0Sstevel@tonic-gate 	icmp = (icmp_t *)q->q_ptr;
2867*0Sstevel@tonic-gate 	if (icmp->icmp_restricted) {
2868*0Sstevel@tonic-gate 		putnext(q, mp);
2869*0Sstevel@tonic-gate 		return;
2870*0Sstevel@tonic-gate 	}
2871*0Sstevel@tonic-gate 
2872*0Sstevel@tonic-gate 	if (mp->b_datap->db_type == M_CTL) {
2873*0Sstevel@tonic-gate 		/*
2874*0Sstevel@tonic-gate 		 * IP sends up the IPSEC_IN message for handling IPSEC
2875*0Sstevel@tonic-gate 		 * policy at the TCP level. We don't need it here.
2876*0Sstevel@tonic-gate 		 */
2877*0Sstevel@tonic-gate 		if (*(uint32_t *)(mp->b_rptr) == IPSEC_IN) {
2878*0Sstevel@tonic-gate 			mp1 = mp->b_cont;
2879*0Sstevel@tonic-gate 			freeb(mp);
2880*0Sstevel@tonic-gate 			mp = mp1;
2881*0Sstevel@tonic-gate 		} else {
2882*0Sstevel@tonic-gate 			pinfo = (in_pktinfo_t *)mp->b_rptr;
2883*0Sstevel@tonic-gate 			if ((icmp->icmp_recvif != 0) &&
2884*0Sstevel@tonic-gate 			    (pinfo->in_pkt_ulp_type == IN_PKTINFO)) {
2885*0Sstevel@tonic-gate 				/*
2886*0Sstevel@tonic-gate 				 * IP has passed the options in mp and the
2887*0Sstevel@tonic-gate 				 * actual data is in b_cont.
2888*0Sstevel@tonic-gate 				 */
2889*0Sstevel@tonic-gate 				recvif = B_TRUE;
2890*0Sstevel@tonic-gate 				/*
2891*0Sstevel@tonic-gate 				 * We are here bcos IP_RECVIF is set so we need
2892*0Sstevel@tonic-gate 				 * to extract the options mblk and adjust the
2893*0Sstevel@tonic-gate 				 * rptr
2894*0Sstevel@tonic-gate 				 */
2895*0Sstevel@tonic-gate 				options_mp = mp;
2896*0Sstevel@tonic-gate 				mp = mp->b_cont;
2897*0Sstevel@tonic-gate 			}
2898*0Sstevel@tonic-gate 		}
2899*0Sstevel@tonic-gate 	}
2900*0Sstevel@tonic-gate 
2901*0Sstevel@tonic-gate 	rptr = mp->b_rptr;
2902*0Sstevel@tonic-gate 	switch (mp->b_datap->db_type) {
2903*0Sstevel@tonic-gate 	case M_DATA:
2904*0Sstevel@tonic-gate 		/*
2905*0Sstevel@tonic-gate 		 * M_DATA messages contain IP packets.  They are handled
2906*0Sstevel@tonic-gate 		 * following the switch.
2907*0Sstevel@tonic-gate 		 */
2908*0Sstevel@tonic-gate 		break;
2909*0Sstevel@tonic-gate 	case M_PROTO:
2910*0Sstevel@tonic-gate 	case M_PCPROTO:
2911*0Sstevel@tonic-gate 		/* M_PROTO messages contain some type of TPI message. */
2912*0Sstevel@tonic-gate 		if ((mp->b_wptr - rptr) < sizeof (t_scalar_t)) {
2913*0Sstevel@tonic-gate 			freemsg(mp);
2914*0Sstevel@tonic-gate 			return;
2915*0Sstevel@tonic-gate 		}
2916*0Sstevel@tonic-gate 		tea = (struct T_error_ack *)rptr;
2917*0Sstevel@tonic-gate 		switch (tea->PRIM_type) {
2918*0Sstevel@tonic-gate 		case T_ERROR_ACK:
2919*0Sstevel@tonic-gate 			switch (tea->ERROR_prim) {
2920*0Sstevel@tonic-gate 			case O_T_BIND_REQ:
2921*0Sstevel@tonic-gate 			case T_BIND_REQ:
2922*0Sstevel@tonic-gate 				/*
2923*0Sstevel@tonic-gate 				 * If our O_T_BIND_REQ/T_BIND_REQ fails,
2924*0Sstevel@tonic-gate 				 * clear out the source address before
2925*0Sstevel@tonic-gate 				 * passing the message upstream.
2926*0Sstevel@tonic-gate 				 * If this was caused by a T_CONN_REQ
2927*0Sstevel@tonic-gate 				 * revert back to bound state.
2928*0Sstevel@tonic-gate 				 */
2929*0Sstevel@tonic-gate 				if (icmp->icmp_state == TS_UNBND) {
2930*0Sstevel@tonic-gate 					/*
2931*0Sstevel@tonic-gate 					 * TPI has not yet bound - bind sent by
2932*0Sstevel@tonic-gate 					 * icmp_bind_proto.
2933*0Sstevel@tonic-gate 					 */
2934*0Sstevel@tonic-gate 					freemsg(mp);
2935*0Sstevel@tonic-gate 					return;
2936*0Sstevel@tonic-gate 				}
2937*0Sstevel@tonic-gate 				if (icmp->icmp_state == TS_DATA_XFER) {
2938*0Sstevel@tonic-gate 					/* Connect failed */
2939*0Sstevel@tonic-gate 					tea->ERROR_prim = T_CONN_REQ;
2940*0Sstevel@tonic-gate 					icmp->icmp_v6src =
2941*0Sstevel@tonic-gate 					    icmp->icmp_bound_v6src;
2942*0Sstevel@tonic-gate 					icmp->icmp_state = TS_IDLE;
2943*0Sstevel@tonic-gate 					if (icmp->icmp_family == AF_INET6)
2944*0Sstevel@tonic-gate 						(void) icmp_build_hdrs(q, icmp);
2945*0Sstevel@tonic-gate 					break;
2946*0Sstevel@tonic-gate 				}
2947*0Sstevel@tonic-gate 
2948*0Sstevel@tonic-gate 				if (icmp->icmp_discon_pending) {
2949*0Sstevel@tonic-gate 					tea->ERROR_prim = T_DISCON_REQ;
2950*0Sstevel@tonic-gate 					icmp->icmp_discon_pending = 0;
2951*0Sstevel@tonic-gate 				}
2952*0Sstevel@tonic-gate 				V6_SET_ZERO(icmp->icmp_v6src);
2953*0Sstevel@tonic-gate 				V6_SET_ZERO(icmp->icmp_bound_v6src);
2954*0Sstevel@tonic-gate 				icmp->icmp_state = TS_UNBND;
2955*0Sstevel@tonic-gate 				if (icmp->icmp_family == AF_INET6)
2956*0Sstevel@tonic-gate 					(void) icmp_build_hdrs(q, icmp);
2957*0Sstevel@tonic-gate 				break;
2958*0Sstevel@tonic-gate 			default:
2959*0Sstevel@tonic-gate 				break;
2960*0Sstevel@tonic-gate 			}
2961*0Sstevel@tonic-gate 			break;
2962*0Sstevel@tonic-gate 		case T_BIND_ACK:
2963*0Sstevel@tonic-gate 			icmp_rput_bind_ack(q, mp);
2964*0Sstevel@tonic-gate 			return;
2965*0Sstevel@tonic-gate 
2966*0Sstevel@tonic-gate 		case T_OPTMGMT_ACK:
2967*0Sstevel@tonic-gate 		case T_OK_ACK:
2968*0Sstevel@tonic-gate 			if (tea->PRIM_type == T_OK_ACK) {
2969*0Sstevel@tonic-gate 				struct T_ok_ack *toa;
2970*0Sstevel@tonic-gate 				toa = (struct T_ok_ack *)rptr;
2971*0Sstevel@tonic-gate 				if (toa->CORRECT_prim == T_UNBIND_REQ) {
2972*0Sstevel@tonic-gate 					/*
2973*0Sstevel@tonic-gate 					 * If somebody sets IPSEC options, IP
2974*0Sstevel@tonic-gate 					 * sends some IPSEC info which is used
2975*0Sstevel@tonic-gate 					 * by the TCP for detached connections.
2976*0Sstevel@tonic-gate 					 * We don't need it here.
2977*0Sstevel@tonic-gate 					 */
2978*0Sstevel@tonic-gate 					if ((mp1 = mp->b_cont) != NULL) {
2979*0Sstevel@tonic-gate 						freemsg(mp1);
2980*0Sstevel@tonic-gate 						mp->b_cont = NULL;
2981*0Sstevel@tonic-gate 					}
2982*0Sstevel@tonic-gate 				}
2983*0Sstevel@tonic-gate 			}
2984*0Sstevel@tonic-gate 			break;
2985*0Sstevel@tonic-gate 		default:
2986*0Sstevel@tonic-gate 			freemsg(mp);
2987*0Sstevel@tonic-gate 			return;
2988*0Sstevel@tonic-gate 		}
2989*0Sstevel@tonic-gate 		putnext(q, mp);
2990*0Sstevel@tonic-gate 		return;
2991*0Sstevel@tonic-gate 	case M_CTL:
2992*0Sstevel@tonic-gate 		if (recvif) {
2993*0Sstevel@tonic-gate 			/*
2994*0Sstevel@tonic-gate 			 * IP has passed the options in mp and the actual data
2995*0Sstevel@tonic-gate 			 * is in b_cont. Jump to normal data processing.
2996*0Sstevel@tonic-gate 			 */
2997*0Sstevel@tonic-gate 			break;
2998*0Sstevel@tonic-gate 		}
2999*0Sstevel@tonic-gate 
3000*0Sstevel@tonic-gate 		/* Contains ICMP packet from IP */
3001*0Sstevel@tonic-gate 		icmp_icmp_error(q, mp);
3002*0Sstevel@tonic-gate 		return;
3003*0Sstevel@tonic-gate 	default:
3004*0Sstevel@tonic-gate 		putnext(q, mp);
3005*0Sstevel@tonic-gate 		return;
3006*0Sstevel@tonic-gate 	}
3007*0Sstevel@tonic-gate 
3008*0Sstevel@tonic-gate 	/*
3009*0Sstevel@tonic-gate 	 * Discard message if it is misaligned or smaller than the IP header.
3010*0Sstevel@tonic-gate 	 */
3011*0Sstevel@tonic-gate 	if (!OK_32PTR(rptr) || (mp->b_wptr - rptr) < sizeof (ipha_t)) {
3012*0Sstevel@tonic-gate 		freemsg(mp);
3013*0Sstevel@tonic-gate 		if (options_mp != NULL)
3014*0Sstevel@tonic-gate 			freeb(options_mp);
3015*0Sstevel@tonic-gate 		BUMP_MIB(&rawip_mib, rawipInErrors);
3016*0Sstevel@tonic-gate 		return;
3017*0Sstevel@tonic-gate 	}
3018*0Sstevel@tonic-gate 	ipvers = IPH_HDR_VERSION((ipha_t *)rptr);
3019*0Sstevel@tonic-gate 
3020*0Sstevel@tonic-gate 	/* Handle M_DATA messages containing IP packets messages */
3021*0Sstevel@tonic-gate 	if (ipvers == IPV4_VERSION) {
3022*0Sstevel@tonic-gate 		/*
3023*0Sstevel@tonic-gate 		 * Special case where IP attaches
3024*0Sstevel@tonic-gate 		 * the IRE needs to be handled so that we don't send up
3025*0Sstevel@tonic-gate 		 * IRE to the user land.
3026*0Sstevel@tonic-gate 		 */
3027*0Sstevel@tonic-gate 		ipha = (ipha_t *)rptr;
3028*0Sstevel@tonic-gate 		hdr_len = IPH_HDR_LENGTH(ipha);
3029*0Sstevel@tonic-gate 
3030*0Sstevel@tonic-gate 		if (ipha->ipha_protocol == IPPROTO_TCP) {
3031*0Sstevel@tonic-gate 			tcph_t *tcph = (tcph_t *)&mp->b_rptr[hdr_len];
3032*0Sstevel@tonic-gate 
3033*0Sstevel@tonic-gate 			if (((tcph->th_flags[0] & (TH_SYN|TH_ACK)) ==
3034*0Sstevel@tonic-gate 			    TH_SYN) && mp->b_cont != NULL) {
3035*0Sstevel@tonic-gate 				mp1 = mp->b_cont;
3036*0Sstevel@tonic-gate 				if (mp1->b_datap->db_type == IRE_DB_TYPE) {
3037*0Sstevel@tonic-gate 					freeb(mp1);
3038*0Sstevel@tonic-gate 					mp->b_cont = NULL;
3039*0Sstevel@tonic-gate 				}
3040*0Sstevel@tonic-gate 			}
3041*0Sstevel@tonic-gate 		}
3042*0Sstevel@tonic-gate 		if (icmp_bsd_compat) {
3043*0Sstevel@tonic-gate 			ushort_t len;
3044*0Sstevel@tonic-gate 			len = ntohs(ipha->ipha_length);
3045*0Sstevel@tonic-gate 
3046*0Sstevel@tonic-gate 			if (mp->b_datap->db_ref > 1) {
3047*0Sstevel@tonic-gate 				/*
3048*0Sstevel@tonic-gate 				 * Allocate a new IP header so that we can
3049*0Sstevel@tonic-gate 				 * modify ipha_length.
3050*0Sstevel@tonic-gate 				 */
3051*0Sstevel@tonic-gate 				mblk_t	*mp1;
3052*0Sstevel@tonic-gate 
3053*0Sstevel@tonic-gate 				mp1 = allocb(hdr_len, BPRI_MED);
3054*0Sstevel@tonic-gate 				if (!mp1) {
3055*0Sstevel@tonic-gate 					freemsg(mp);
3056*0Sstevel@tonic-gate 					if (options_mp != NULL)
3057*0Sstevel@tonic-gate 						freeb(options_mp);
3058*0Sstevel@tonic-gate 					BUMP_MIB(&rawip_mib, rawipInErrors);
3059*0Sstevel@tonic-gate 					return;
3060*0Sstevel@tonic-gate 				}
3061*0Sstevel@tonic-gate 				bcopy(rptr, mp1->b_rptr, hdr_len);
3062*0Sstevel@tonic-gate 				mp->b_rptr = rptr + hdr_len;
3063*0Sstevel@tonic-gate 				rptr = mp1->b_rptr;
3064*0Sstevel@tonic-gate 				ipha = (ipha_t *)rptr;
3065*0Sstevel@tonic-gate 				mp1->b_cont = mp;
3066*0Sstevel@tonic-gate 				mp1->b_wptr = rptr + hdr_len;
3067*0Sstevel@tonic-gate 				mp = mp1;
3068*0Sstevel@tonic-gate 			}
3069*0Sstevel@tonic-gate 			len -= hdr_len;
3070*0Sstevel@tonic-gate 			ipha->ipha_length = htons(len);
3071*0Sstevel@tonic-gate 		}
3072*0Sstevel@tonic-gate 	}
3073*0Sstevel@tonic-gate 
3074*0Sstevel@tonic-gate 	/*
3075*0Sstevel@tonic-gate 	 * This is the inbound data path.  Packets are passed upstream as
3076*0Sstevel@tonic-gate 	 * T_UNITDATA_IND messages with full IP headers still attached.
3077*0Sstevel@tonic-gate 	 */
3078*0Sstevel@tonic-gate 	if (icmp->icmp_family == AF_INET) {
3079*0Sstevel@tonic-gate 		ASSERT(ipvers == IPV4_VERSION);
3080*0Sstevel@tonic-gate 		udi_size =  sizeof (struct T_unitdata_ind) + sizeof (sin_t);
3081*0Sstevel@tonic-gate 		if (recvif) {
3082*0Sstevel@tonic-gate 			udi_size += sizeof (struct T_opthdr) +
3083*0Sstevel@tonic-gate 			    sizeof (uint_t);
3084*0Sstevel@tonic-gate 		}
3085*0Sstevel@tonic-gate 		mp1 = allocb(udi_size, BPRI_MED);
3086*0Sstevel@tonic-gate 		if (mp1 == NULL) {
3087*0Sstevel@tonic-gate 			freemsg(mp);
3088*0Sstevel@tonic-gate 			if (options_mp != NULL)
3089*0Sstevel@tonic-gate 				freeb(options_mp);
3090*0Sstevel@tonic-gate 			BUMP_MIB(&rawip_mib, rawipInErrors);
3091*0Sstevel@tonic-gate 			return;
3092*0Sstevel@tonic-gate 		}
3093*0Sstevel@tonic-gate 		mp1->b_cont = mp;
3094*0Sstevel@tonic-gate 		mp = mp1;
3095*0Sstevel@tonic-gate 		tudi = (struct T_unitdata_ind *)mp->b_rptr;
3096*0Sstevel@tonic-gate 		mp->b_datap->db_type = M_PROTO;
3097*0Sstevel@tonic-gate 		mp->b_wptr = (uchar_t *)tudi + udi_size;
3098*0Sstevel@tonic-gate 		tudi->PRIM_type = T_UNITDATA_IND;
3099*0Sstevel@tonic-gate 		tudi->SRC_length = sizeof (sin_t);
3100*0Sstevel@tonic-gate 		tudi->SRC_offset = sizeof (struct T_unitdata_ind);
3101*0Sstevel@tonic-gate 		sin = (sin_t *)&tudi[1];
3102*0Sstevel@tonic-gate 		*sin = sin_null;
3103*0Sstevel@tonic-gate 		sin->sin_family = AF_INET;
3104*0Sstevel@tonic-gate 		sin->sin_addr.s_addr = ipha->ipha_src;
3105*0Sstevel@tonic-gate 		tudi->OPT_offset =  sizeof (struct T_unitdata_ind) +
3106*0Sstevel@tonic-gate 		    sizeof (sin_t);
3107*0Sstevel@tonic-gate 		udi_size -= (sizeof (struct T_unitdata_ind) + sizeof (sin_t));
3108*0Sstevel@tonic-gate 		tudi->OPT_length = udi_size;
3109*0Sstevel@tonic-gate 
3110*0Sstevel@tonic-gate 		/*
3111*0Sstevel@tonic-gate 		 * Add options if IP_RECVIF is set
3112*0Sstevel@tonic-gate 		 */
3113*0Sstevel@tonic-gate 		if (udi_size != 0) {
3114*0Sstevel@tonic-gate 			char *dstopt;
3115*0Sstevel@tonic-gate 
3116*0Sstevel@tonic-gate 			dstopt = (char *)&sin[1];
3117*0Sstevel@tonic-gate 			if (recvif) {
3118*0Sstevel@tonic-gate 
3119*0Sstevel@tonic-gate 				struct T_opthdr *toh;
3120*0Sstevel@tonic-gate 				uint_t		*dstptr;
3121*0Sstevel@tonic-gate 
3122*0Sstevel@tonic-gate 				toh = (struct T_opthdr *)dstopt;
3123*0Sstevel@tonic-gate 				toh->level = IPPROTO_IP;
3124*0Sstevel@tonic-gate 				toh->name = IP_RECVIF;
3125*0Sstevel@tonic-gate 				toh->len = sizeof (struct T_opthdr) +
3126*0Sstevel@tonic-gate 					sizeof (uint_t);
3127*0Sstevel@tonic-gate 				toh->status = 0;
3128*0Sstevel@tonic-gate 				dstopt += sizeof (struct T_opthdr);
3129*0Sstevel@tonic-gate 				dstptr = (uint_t *)dstopt;
3130*0Sstevel@tonic-gate 				*dstptr = pinfo->in_pkt_ifindex;
3131*0Sstevel@tonic-gate 				dstopt += sizeof (uint_t);
3132*0Sstevel@tonic-gate 				freeb(options_mp);
3133*0Sstevel@tonic-gate 				udi_size -= toh->len;
3134*0Sstevel@tonic-gate 			}
3135*0Sstevel@tonic-gate 
3136*0Sstevel@tonic-gate 			/* Consumed all of allocated space */
3137*0Sstevel@tonic-gate 			ASSERT(udi_size == 0);
3138*0Sstevel@tonic-gate 		}
3139*0Sstevel@tonic-gate 
3140*0Sstevel@tonic-gate 		BUMP_MIB(&rawip_mib, rawipInDatagrams);
3141*0Sstevel@tonic-gate 		putnext(q, mp);
3142*0Sstevel@tonic-gate 		return;
3143*0Sstevel@tonic-gate 	}
3144*0Sstevel@tonic-gate 
3145*0Sstevel@tonic-gate 	/*
3146*0Sstevel@tonic-gate 	 * We don't need options_mp in the IPv6 path.
3147*0Sstevel@tonic-gate 	 */
3148*0Sstevel@tonic-gate 	if (options_mp != NULL) {
3149*0Sstevel@tonic-gate 		freeb(options_mp);
3150*0Sstevel@tonic-gate 		options_mp = NULL;
3151*0Sstevel@tonic-gate 	}
3152*0Sstevel@tonic-gate 
3153*0Sstevel@tonic-gate 	/*
3154*0Sstevel@tonic-gate 	 * Discard message if it is smaller than the IPv6 header
3155*0Sstevel@tonic-gate 	 * or if the header is malformed.
3156*0Sstevel@tonic-gate 	 */
3157*0Sstevel@tonic-gate 	if ((mp->b_wptr - rptr) < sizeof (ip6_t) ||
3158*0Sstevel@tonic-gate 	    IPH_HDR_VERSION((ipha_t *)rptr) != IPV6_VERSION ||
3159*0Sstevel@tonic-gate 	    icmp->icmp_family != AF_INET6) {
3160*0Sstevel@tonic-gate 		freemsg(mp);
3161*0Sstevel@tonic-gate 		BUMP_MIB(&rawip_mib, rawipInErrors);
3162*0Sstevel@tonic-gate 		return;
3163*0Sstevel@tonic-gate 	}
3164*0Sstevel@tonic-gate 
3165*0Sstevel@tonic-gate 	/* Initialize */
3166*0Sstevel@tonic-gate 	ipp.ipp_fields = 0;
3167*0Sstevel@tonic-gate 
3168*0Sstevel@tonic-gate 	ip6h = (ip6_t *)rptr;
3169*0Sstevel@tonic-gate 	/*
3170*0Sstevel@tonic-gate 	 * Call on ip_find_hdr_v6 which gets the total hdr len
3171*0Sstevel@tonic-gate 	 * as well as individual lenghts of ext hdrs (and ptrs to
3172*0Sstevel@tonic-gate 	 * them).
3173*0Sstevel@tonic-gate 	 */
3174*0Sstevel@tonic-gate 	if (ip6h->ip6_nxt != icmp->icmp_proto) {
3175*0Sstevel@tonic-gate 		/* Look for ifindex information */
3176*0Sstevel@tonic-gate 		if (ip6h->ip6_nxt == IPPROTO_RAW) {
3177*0Sstevel@tonic-gate 			ip6i = (ip6i_t *)ip6h;
3178*0Sstevel@tonic-gate 			if (ip6i->ip6i_flags & IP6I_IFINDEX) {
3179*0Sstevel@tonic-gate 				ASSERT(ip6i->ip6i_ifindex != 0);
3180*0Sstevel@tonic-gate 				ipp.ipp_fields |= IPPF_IFINDEX;
3181*0Sstevel@tonic-gate 				ipp.ipp_ifindex = ip6i->ip6i_ifindex;
3182*0Sstevel@tonic-gate 			}
3183*0Sstevel@tonic-gate 			rptr = (uchar_t *)&ip6i[1];
3184*0Sstevel@tonic-gate 			mp->b_rptr = rptr;
3185*0Sstevel@tonic-gate 			if (rptr == mp->b_wptr) {
3186*0Sstevel@tonic-gate 				mp1 = mp->b_cont;
3187*0Sstevel@tonic-gate 				freeb(mp);
3188*0Sstevel@tonic-gate 				mp = mp1;
3189*0Sstevel@tonic-gate 				rptr = mp->b_rptr;
3190*0Sstevel@tonic-gate 			}
3191*0Sstevel@tonic-gate 			ASSERT(mp->b_wptr - rptr >= IPV6_HDR_LEN);
3192*0Sstevel@tonic-gate 			ip6h = (ip6_t *)rptr;
3193*0Sstevel@tonic-gate 		}
3194*0Sstevel@tonic-gate 		hdr_len = ip_find_hdr_v6(mp, ip6h, &ipp, &nexthdr);
3195*0Sstevel@tonic-gate 	} else {
3196*0Sstevel@tonic-gate 		hdr_len = IPV6_HDR_LEN;
3197*0Sstevel@tonic-gate 		ip6i = NULL;
3198*0Sstevel@tonic-gate 		nexthdr = ip6h->ip6_nxt;
3199*0Sstevel@tonic-gate 	}
3200*0Sstevel@tonic-gate 	/*
3201*0Sstevel@tonic-gate 	 * One special case where IP attaches the IRE needs to
3202*0Sstevel@tonic-gate 	 * be handled so that we don't send up IRE to the user land.
3203*0Sstevel@tonic-gate 	 */
3204*0Sstevel@tonic-gate 	if (nexthdr == IPPROTO_TCP) {
3205*0Sstevel@tonic-gate 		tcph_t *tcph = (tcph_t *)&mp->b_rptr[hdr_len];
3206*0Sstevel@tonic-gate 
3207*0Sstevel@tonic-gate 		if (((tcph->th_flags[0] & (TH_SYN|TH_ACK)) == TH_SYN) &&
3208*0Sstevel@tonic-gate 		    mp->b_cont != NULL) {
3209*0Sstevel@tonic-gate 			mp1 = mp->b_cont;
3210*0Sstevel@tonic-gate 			if (mp1->b_datap->db_type == IRE_DB_TYPE) {
3211*0Sstevel@tonic-gate 				freeb(mp1);
3212*0Sstevel@tonic-gate 				mp->b_cont = NULL;
3213*0Sstevel@tonic-gate 			}
3214*0Sstevel@tonic-gate 		}
3215*0Sstevel@tonic-gate 	}
3216*0Sstevel@tonic-gate 	/*
3217*0Sstevel@tonic-gate 	 * Check a filter for ICMPv6 types if needed.
3218*0Sstevel@tonic-gate 	 * Verify raw checksums if needed.
3219*0Sstevel@tonic-gate 	 */
3220*0Sstevel@tonic-gate 	if (icmp->icmp_filter != NULL || icmp->icmp_raw_checksum) {
3221*0Sstevel@tonic-gate 		if (icmp->icmp_filter != NULL) {
3222*0Sstevel@tonic-gate 			int type;
3223*0Sstevel@tonic-gate 
3224*0Sstevel@tonic-gate 			/* Assumes that IP has done the pullupmsg */
3225*0Sstevel@tonic-gate 			type = mp->b_rptr[hdr_len];
3226*0Sstevel@tonic-gate 
3227*0Sstevel@tonic-gate 			ASSERT(mp->b_rptr + hdr_len <= mp->b_wptr);
3228*0Sstevel@tonic-gate 			if (ICMP6_FILTER_WILLBLOCK(type, icmp->icmp_filter)) {
3229*0Sstevel@tonic-gate 				freemsg(mp);
3230*0Sstevel@tonic-gate 				return;
3231*0Sstevel@tonic-gate 			}
3232*0Sstevel@tonic-gate 		} else {
3233*0Sstevel@tonic-gate 			/* Checksum */
3234*0Sstevel@tonic-gate 			uint16_t	*up;
3235*0Sstevel@tonic-gate 			uint32_t	sum;
3236*0Sstevel@tonic-gate 			int		remlen;
3237*0Sstevel@tonic-gate 
3238*0Sstevel@tonic-gate 			up = (uint16_t *)&ip6h->ip6_src;
3239*0Sstevel@tonic-gate 
3240*0Sstevel@tonic-gate 			remlen = msgdsize(mp) - hdr_len;
3241*0Sstevel@tonic-gate 			sum = htons(icmp->icmp_proto + remlen)
3242*0Sstevel@tonic-gate 			    + up[0] + up[1] + up[2] + up[3]
3243*0Sstevel@tonic-gate 			    + up[4] + up[5] + up[6] + up[7]
3244*0Sstevel@tonic-gate 			    + up[8] + up[9] + up[10] + up[11]
3245*0Sstevel@tonic-gate 			    + up[12] + up[13] + up[14] + up[15];
3246*0Sstevel@tonic-gate 			sum = (sum & 0xffff) + (sum >> 16);
3247*0Sstevel@tonic-gate 			sum = IP_CSUM(mp, hdr_len, sum);
3248*0Sstevel@tonic-gate 			if (sum != 0) {
3249*0Sstevel@tonic-gate 				/* IPv6 RAW checksum failed */
3250*0Sstevel@tonic-gate 				ip0dbg(("icmp_rput: RAW checksum "
3251*0Sstevel@tonic-gate 				    "failed %x\n", sum));
3252*0Sstevel@tonic-gate 				freemsg(mp);
3253*0Sstevel@tonic-gate 				BUMP_MIB(&rawip_mib, rawipInCksumErrs);
3254*0Sstevel@tonic-gate 				return;
3255*0Sstevel@tonic-gate 			}
3256*0Sstevel@tonic-gate 		}
3257*0Sstevel@tonic-gate 	}
3258*0Sstevel@tonic-gate 	/* Skip all the IPv6 headers per API */
3259*0Sstevel@tonic-gate 	mp->b_rptr += hdr_len;
3260*0Sstevel@tonic-gate 
3261*0Sstevel@tonic-gate 	udi_size = sizeof (struct T_unitdata_ind) + sizeof (sin6_t);
3262*0Sstevel@tonic-gate 
3263*0Sstevel@tonic-gate 	/*
3264*0Sstevel@tonic-gate 	 * We use local variables icmp_opt and icmp_ipv6_recvhoplimit to
3265*0Sstevel@tonic-gate 	 * maintain state information, instead of relying on icmp_t
3266*0Sstevel@tonic-gate 	 * structure, since there arent any locks protecting these members
3267*0Sstevel@tonic-gate 	 * and there is a window where there might be a race between a
3268*0Sstevel@tonic-gate 	 * thread setting options on the write side and a thread reading
3269*0Sstevel@tonic-gate 	 * these options on the read size.
3270*0Sstevel@tonic-gate 	 */
3271*0Sstevel@tonic-gate 	if (ipp.ipp_fields & (IPPF_HOPOPTS|IPPF_DSTOPTS|IPPF_RTDSTOPTS|
3272*0Sstevel@tonic-gate 	    IPPF_RTHDR|IPPF_IFINDEX)) {
3273*0Sstevel@tonic-gate 		if (icmp->icmp_ipv6_recvhopopts &&
3274*0Sstevel@tonic-gate 		    (ipp.ipp_fields & IPPF_HOPOPTS)) {
3275*0Sstevel@tonic-gate 			udi_size += sizeof (struct T_opthdr) +
3276*0Sstevel@tonic-gate 			    ipp.ipp_hopoptslen;
3277*0Sstevel@tonic-gate 			icmp_opt |= IPPF_HOPOPTS;
3278*0Sstevel@tonic-gate 		}
3279*0Sstevel@tonic-gate 		if ((icmp->icmp_ipv6_recvdstopts ||
3280*0Sstevel@tonic-gate 			icmp->icmp_old_ipv6_recvdstopts) &&
3281*0Sstevel@tonic-gate 		    (ipp.ipp_fields & IPPF_DSTOPTS)) {
3282*0Sstevel@tonic-gate 			udi_size += sizeof (struct T_opthdr) +
3283*0Sstevel@tonic-gate 			    ipp.ipp_dstoptslen;
3284*0Sstevel@tonic-gate 			icmp_opt |= IPPF_DSTOPTS;
3285*0Sstevel@tonic-gate 		}
3286*0Sstevel@tonic-gate 		if (((icmp->icmp_ipv6_recvdstopts &&
3287*0Sstevel@tonic-gate 		    icmp->icmp_ipv6_recvrthdr &&
3288*0Sstevel@tonic-gate 		    (ipp.ipp_fields & IPPF_RTHDR)) ||
3289*0Sstevel@tonic-gate 		    icmp->icmp_ipv6_recvrtdstopts) &&
3290*0Sstevel@tonic-gate 		    (ipp.ipp_fields & IPPF_RTDSTOPTS)) {
3291*0Sstevel@tonic-gate 			udi_size += sizeof (struct T_opthdr) +
3292*0Sstevel@tonic-gate 			    ipp.ipp_rtdstoptslen;
3293*0Sstevel@tonic-gate 			icmp_opt |= IPPF_RTDSTOPTS;
3294*0Sstevel@tonic-gate 		}
3295*0Sstevel@tonic-gate 		if (icmp->icmp_ipv6_recvrthdr &&
3296*0Sstevel@tonic-gate 		    (ipp.ipp_fields & IPPF_RTHDR)) {
3297*0Sstevel@tonic-gate 			udi_size += sizeof (struct T_opthdr) +
3298*0Sstevel@tonic-gate 			    ipp.ipp_rthdrlen;
3299*0Sstevel@tonic-gate 			icmp_opt |= IPPF_RTHDR;
3300*0Sstevel@tonic-gate 		}
3301*0Sstevel@tonic-gate 		if (icmp->icmp_ipv6_recvpktinfo &&
3302*0Sstevel@tonic-gate 		    (ipp.ipp_fields & IPPF_IFINDEX)) {
3303*0Sstevel@tonic-gate 			udi_size += sizeof (struct T_opthdr) +
3304*0Sstevel@tonic-gate 			    sizeof (struct in6_pktinfo);
3305*0Sstevel@tonic-gate 			icmp_opt |= IPPF_IFINDEX;
3306*0Sstevel@tonic-gate 		}
3307*0Sstevel@tonic-gate 	}
3308*0Sstevel@tonic-gate 	if (icmp->icmp_ipv6_recvhoplimit) {
3309*0Sstevel@tonic-gate 		udi_size += sizeof (struct T_opthdr) + sizeof (int);
3310*0Sstevel@tonic-gate 		icmp_ipv6_recvhoplimit = B_TRUE;
3311*0Sstevel@tonic-gate 	}
3312*0Sstevel@tonic-gate 
3313*0Sstevel@tonic-gate 	if (icmp->icmp_ipv6_recvtclass)
3314*0Sstevel@tonic-gate 		udi_size += sizeof (struct T_opthdr) + sizeof (int);
3315*0Sstevel@tonic-gate 
3316*0Sstevel@tonic-gate 	mp1 = allocb(udi_size, BPRI_MED);
3317*0Sstevel@tonic-gate 	if (mp1 == NULL) {
3318*0Sstevel@tonic-gate 		freemsg(mp);
3319*0Sstevel@tonic-gate 		BUMP_MIB(&rawip_mib, rawipInErrors);
3320*0Sstevel@tonic-gate 		return;
3321*0Sstevel@tonic-gate 	}
3322*0Sstevel@tonic-gate 	mp1->b_cont = mp;
3323*0Sstevel@tonic-gate 	mp = mp1;
3324*0Sstevel@tonic-gate 	mp->b_datap->db_type = M_PROTO;
3325*0Sstevel@tonic-gate 	tudi = (struct T_unitdata_ind *)mp->b_rptr;
3326*0Sstevel@tonic-gate 	mp->b_wptr = (uchar_t *)tudi + udi_size;
3327*0Sstevel@tonic-gate 	tudi->PRIM_type = T_UNITDATA_IND;
3328*0Sstevel@tonic-gate 	tudi->SRC_length = sizeof (sin6_t);
3329*0Sstevel@tonic-gate 	tudi->SRC_offset = sizeof (struct T_unitdata_ind);
3330*0Sstevel@tonic-gate 	tudi->OPT_offset = sizeof (struct T_unitdata_ind) + sizeof (sin6_t);
3331*0Sstevel@tonic-gate 	udi_size -= (sizeof (struct T_unitdata_ind) + sizeof (sin6_t));
3332*0Sstevel@tonic-gate 	tudi->OPT_length = udi_size;
3333*0Sstevel@tonic-gate 	sin6 = (sin6_t *)&tudi[1];
3334*0Sstevel@tonic-gate 	sin6->sin6_port = 0;
3335*0Sstevel@tonic-gate 	sin6->sin6_family = AF_INET6;
3336*0Sstevel@tonic-gate 
3337*0Sstevel@tonic-gate 	sin6->sin6_addr = ip6h->ip6_src;
3338*0Sstevel@tonic-gate 	/* No sin6_flowinfo per API */
3339*0Sstevel@tonic-gate 	sin6->sin6_flowinfo = 0;
3340*0Sstevel@tonic-gate 	/* For link-scope source pass up scope id */
3341*0Sstevel@tonic-gate 	if ((ipp.ipp_fields & IPPF_IFINDEX) &&
3342*0Sstevel@tonic-gate 	    IN6_IS_ADDR_LINKSCOPE(&ip6h->ip6_src))
3343*0Sstevel@tonic-gate 		sin6->sin6_scope_id = ipp.ipp_ifindex;
3344*0Sstevel@tonic-gate 	else
3345*0Sstevel@tonic-gate 		sin6->sin6_scope_id = 0;
3346*0Sstevel@tonic-gate 
3347*0Sstevel@tonic-gate 	sin6->__sin6_src_id = ip_srcid_find_addr(&ip6h->ip6_dst,
3348*0Sstevel@tonic-gate 	    icmp->icmp_zoneid);
3349*0Sstevel@tonic-gate 
3350*0Sstevel@tonic-gate 	if (udi_size != 0) {
3351*0Sstevel@tonic-gate 		uchar_t *dstopt;
3352*0Sstevel@tonic-gate 
3353*0Sstevel@tonic-gate 		dstopt = (uchar_t *)&sin6[1];
3354*0Sstevel@tonic-gate 		if (icmp_opt & IPPF_IFINDEX) {
3355*0Sstevel@tonic-gate 			struct T_opthdr *toh;
3356*0Sstevel@tonic-gate 			struct in6_pktinfo *pkti;
3357*0Sstevel@tonic-gate 
3358*0Sstevel@tonic-gate 			toh = (struct T_opthdr *)dstopt;
3359*0Sstevel@tonic-gate 			toh->level = IPPROTO_IPV6;
3360*0Sstevel@tonic-gate 			toh->name = IPV6_PKTINFO;
3361*0Sstevel@tonic-gate 			toh->len = sizeof (struct T_opthdr) +
3362*0Sstevel@tonic-gate 			    sizeof (*pkti);
3363*0Sstevel@tonic-gate 			toh->status = 0;
3364*0Sstevel@tonic-gate 			dstopt += sizeof (struct T_opthdr);
3365*0Sstevel@tonic-gate 			pkti = (struct in6_pktinfo *)dstopt;
3366*0Sstevel@tonic-gate 			pkti->ipi6_addr = ip6h->ip6_dst;
3367*0Sstevel@tonic-gate 			pkti->ipi6_ifindex = ipp.ipp_ifindex;
3368*0Sstevel@tonic-gate 			dstopt += sizeof (*pkti);
3369*0Sstevel@tonic-gate 			udi_size -= toh->len;
3370*0Sstevel@tonic-gate 		}
3371*0Sstevel@tonic-gate 		if (icmp_ipv6_recvhoplimit) {
3372*0Sstevel@tonic-gate 			struct T_opthdr *toh;
3373*0Sstevel@tonic-gate 
3374*0Sstevel@tonic-gate 			toh = (struct T_opthdr *)dstopt;
3375*0Sstevel@tonic-gate 			toh->level = IPPROTO_IPV6;
3376*0Sstevel@tonic-gate 			toh->name = IPV6_HOPLIMIT;
3377*0Sstevel@tonic-gate 			toh->len = sizeof (struct T_opthdr) +
3378*0Sstevel@tonic-gate 			    sizeof (uint_t);
3379*0Sstevel@tonic-gate 			toh->status = 0;
3380*0Sstevel@tonic-gate 			dstopt += sizeof (struct T_opthdr);
3381*0Sstevel@tonic-gate 			*(uint_t *)dstopt = ip6h->ip6_hops;
3382*0Sstevel@tonic-gate 			dstopt += sizeof (uint_t);
3383*0Sstevel@tonic-gate 			udi_size -= toh->len;
3384*0Sstevel@tonic-gate 		}
3385*0Sstevel@tonic-gate 		if (icmp->icmp_ipv6_recvtclass) {
3386*0Sstevel@tonic-gate 			struct T_opthdr *toh;
3387*0Sstevel@tonic-gate 
3388*0Sstevel@tonic-gate 			toh = (struct T_opthdr *)dstopt;
3389*0Sstevel@tonic-gate 			toh->level = IPPROTO_IPV6;
3390*0Sstevel@tonic-gate 			toh->name = IPV6_TCLASS;
3391*0Sstevel@tonic-gate 			toh->len = sizeof (struct T_opthdr) +
3392*0Sstevel@tonic-gate 			    sizeof (uint_t);
3393*0Sstevel@tonic-gate 			toh->status = 0;
3394*0Sstevel@tonic-gate 			dstopt += sizeof (struct T_opthdr);
3395*0Sstevel@tonic-gate 			*(uint_t *)dstopt = IPV6_FLOW_TCLASS(ip6h->ip6_flow);
3396*0Sstevel@tonic-gate 			dstopt += sizeof (uint_t);
3397*0Sstevel@tonic-gate 			udi_size -= toh->len;
3398*0Sstevel@tonic-gate 		}
3399*0Sstevel@tonic-gate 		if (icmp_opt & IPPF_HOPOPTS) {
3400*0Sstevel@tonic-gate 			struct T_opthdr *toh;
3401*0Sstevel@tonic-gate 
3402*0Sstevel@tonic-gate 			toh = (struct T_opthdr *)dstopt;
3403*0Sstevel@tonic-gate 			toh->level = IPPROTO_IPV6;
3404*0Sstevel@tonic-gate 			toh->name = IPV6_HOPOPTS;
3405*0Sstevel@tonic-gate 			toh->len = sizeof (struct T_opthdr) +
3406*0Sstevel@tonic-gate 			    ipp.ipp_hopoptslen;
3407*0Sstevel@tonic-gate 			toh->status = 0;
3408*0Sstevel@tonic-gate 			dstopt += sizeof (struct T_opthdr);
3409*0Sstevel@tonic-gate 			bcopy(ipp.ipp_hopopts, dstopt,
3410*0Sstevel@tonic-gate 			    ipp.ipp_hopoptslen);
3411*0Sstevel@tonic-gate 			dstopt += ipp.ipp_hopoptslen;
3412*0Sstevel@tonic-gate 			udi_size -= toh->len;
3413*0Sstevel@tonic-gate 		}
3414*0Sstevel@tonic-gate 		if (icmp_opt & IPPF_RTDSTOPTS) {
3415*0Sstevel@tonic-gate 			struct T_opthdr *toh;
3416*0Sstevel@tonic-gate 
3417*0Sstevel@tonic-gate 			toh = (struct T_opthdr *)dstopt;
3418*0Sstevel@tonic-gate 			toh->level = IPPROTO_IPV6;
3419*0Sstevel@tonic-gate 			toh->name = IPV6_DSTOPTS;
3420*0Sstevel@tonic-gate 			toh->len = sizeof (struct T_opthdr) +
3421*0Sstevel@tonic-gate 			    ipp.ipp_rtdstoptslen;
3422*0Sstevel@tonic-gate 			toh->status = 0;
3423*0Sstevel@tonic-gate 			dstopt += sizeof (struct T_opthdr);
3424*0Sstevel@tonic-gate 			bcopy(ipp.ipp_rtdstopts, dstopt,
3425*0Sstevel@tonic-gate 			    ipp.ipp_rtdstoptslen);
3426*0Sstevel@tonic-gate 			dstopt += ipp.ipp_rtdstoptslen;
3427*0Sstevel@tonic-gate 			udi_size -= toh->len;
3428*0Sstevel@tonic-gate 		}
3429*0Sstevel@tonic-gate 		if (icmp_opt & IPPF_RTHDR) {
3430*0Sstevel@tonic-gate 			struct T_opthdr *toh;
3431*0Sstevel@tonic-gate 
3432*0Sstevel@tonic-gate 			toh = (struct T_opthdr *)dstopt;
3433*0Sstevel@tonic-gate 			toh->level = IPPROTO_IPV6;
3434*0Sstevel@tonic-gate 			toh->name = IPV6_RTHDR;
3435*0Sstevel@tonic-gate 			toh->len = sizeof (struct T_opthdr) +
3436*0Sstevel@tonic-gate 			    ipp.ipp_rthdrlen;
3437*0Sstevel@tonic-gate 			toh->status = 0;
3438*0Sstevel@tonic-gate 			dstopt += sizeof (struct T_opthdr);
3439*0Sstevel@tonic-gate 			bcopy(ipp.ipp_rthdr, dstopt, ipp.ipp_rthdrlen);
3440*0Sstevel@tonic-gate 			dstopt += ipp.ipp_rthdrlen;
3441*0Sstevel@tonic-gate 			udi_size -= toh->len;
3442*0Sstevel@tonic-gate 		}
3443*0Sstevel@tonic-gate 		if (icmp_opt & IPPF_DSTOPTS) {
3444*0Sstevel@tonic-gate 			struct T_opthdr *toh;
3445*0Sstevel@tonic-gate 
3446*0Sstevel@tonic-gate 			toh = (struct T_opthdr *)dstopt;
3447*0Sstevel@tonic-gate 			toh->level = IPPROTO_IPV6;
3448*0Sstevel@tonic-gate 			toh->name = IPV6_DSTOPTS;
3449*0Sstevel@tonic-gate 			toh->len = sizeof (struct T_opthdr) +
3450*0Sstevel@tonic-gate 			    ipp.ipp_dstoptslen;
3451*0Sstevel@tonic-gate 			toh->status = 0;
3452*0Sstevel@tonic-gate 			dstopt += sizeof (struct T_opthdr);
3453*0Sstevel@tonic-gate 			bcopy(ipp.ipp_dstopts, dstopt,
3454*0Sstevel@tonic-gate 			    ipp.ipp_dstoptslen);
3455*0Sstevel@tonic-gate 			dstopt += ipp.ipp_dstoptslen;
3456*0Sstevel@tonic-gate 			udi_size -= toh->len;
3457*0Sstevel@tonic-gate 		}
3458*0Sstevel@tonic-gate 		/* Consumed all of allocated space */
3459*0Sstevel@tonic-gate 		ASSERT(udi_size == 0);
3460*0Sstevel@tonic-gate 	}
3461*0Sstevel@tonic-gate 	BUMP_MIB(&rawip_mib, rawipInDatagrams);
3462*0Sstevel@tonic-gate 	putnext(q, mp);
3463*0Sstevel@tonic-gate }
3464*0Sstevel@tonic-gate 
3465*0Sstevel@tonic-gate /*
3466*0Sstevel@tonic-gate  * Process a T_BIND_ACK
3467*0Sstevel@tonic-gate  */
3468*0Sstevel@tonic-gate static void
3469*0Sstevel@tonic-gate icmp_rput_bind_ack(queue_t *q, mblk_t *mp)
3470*0Sstevel@tonic-gate {
3471*0Sstevel@tonic-gate 	icmp_t	*icmp = (icmp_t *)q->q_ptr;
3472*0Sstevel@tonic-gate 	mblk_t	*mp1;
3473*0Sstevel@tonic-gate 	ire_t	*ire;
3474*0Sstevel@tonic-gate 	struct T_bind_ack *tba;
3475*0Sstevel@tonic-gate 	uchar_t *addrp;
3476*0Sstevel@tonic-gate 	ipa_conn_t	*ac;
3477*0Sstevel@tonic-gate 	ipa6_conn_t	*ac6;
3478*0Sstevel@tonic-gate 
3479*0Sstevel@tonic-gate 	/*
3480*0Sstevel@tonic-gate 	 * We know if headers are included or not so we can
3481*0Sstevel@tonic-gate 	 * safely do this.
3482*0Sstevel@tonic-gate 	 */
3483*0Sstevel@tonic-gate 	if (icmp->icmp_state == TS_UNBND) {
3484*0Sstevel@tonic-gate 		/*
3485*0Sstevel@tonic-gate 		 * TPI has not yet bound - bind sent by
3486*0Sstevel@tonic-gate 		 * icmp_bind_proto.
3487*0Sstevel@tonic-gate 		 */
3488*0Sstevel@tonic-gate 		freemsg(mp);
3489*0Sstevel@tonic-gate 		return;
3490*0Sstevel@tonic-gate 	}
3491*0Sstevel@tonic-gate 	if (icmp->icmp_discon_pending)
3492*0Sstevel@tonic-gate 		icmp->icmp_discon_pending = 0;
3493*0Sstevel@tonic-gate 
3494*0Sstevel@tonic-gate 	/*
3495*0Sstevel@tonic-gate 	 * If a broadcast/multicast address was bound set
3496*0Sstevel@tonic-gate 	 * the source address to 0.
3497*0Sstevel@tonic-gate 	 * This ensures no datagrams with broadcast address
3498*0Sstevel@tonic-gate 	 * as source address are emitted (which would violate
3499*0Sstevel@tonic-gate 	 * RFC1122 - Hosts requirements)
3500*0Sstevel@tonic-gate 	 *
3501*0Sstevel@tonic-gate 	 * Note that when connecting the returned IRE is
3502*0Sstevel@tonic-gate 	 * for the destination address and we only perform
3503*0Sstevel@tonic-gate 	 * the broadcast check for the source address (it
3504*0Sstevel@tonic-gate 	 * is OK to connect to a broadcast/multicast address.)
3505*0Sstevel@tonic-gate 	 */
3506*0Sstevel@tonic-gate 	mp1 = mp->b_cont;
3507*0Sstevel@tonic-gate 	if (mp1 != NULL && mp1->b_datap->db_type == IRE_DB_TYPE) {
3508*0Sstevel@tonic-gate 		ire = (ire_t *)mp1->b_rptr;
3509*0Sstevel@tonic-gate 
3510*0Sstevel@tonic-gate 		/*
3511*0Sstevel@tonic-gate 		 * Note: we get IRE_BROADCAST for IPv6 to "mark" a multicast
3512*0Sstevel@tonic-gate 		 * local address.
3513*0Sstevel@tonic-gate 		 */
3514*0Sstevel@tonic-gate 		if (ire->ire_type == IRE_BROADCAST &&
3515*0Sstevel@tonic-gate 		    icmp->icmp_state != TS_DATA_XFER) {
3516*0Sstevel@tonic-gate 			/* This was just a local bind to a MC/broadcast addr */
3517*0Sstevel@tonic-gate 			V6_SET_ZERO(icmp->icmp_v6src);
3518*0Sstevel@tonic-gate 			if (icmp->icmp_family == AF_INET6)
3519*0Sstevel@tonic-gate 				(void) icmp_build_hdrs(q, icmp);
3520*0Sstevel@tonic-gate 		} else if (V6_OR_V4_INADDR_ANY(icmp->icmp_v6src)) {
3521*0Sstevel@tonic-gate 			/*
3522*0Sstevel@tonic-gate 			 * Local address not yet set - pick it from the
3523*0Sstevel@tonic-gate 			 * T_bind_ack
3524*0Sstevel@tonic-gate 			 */
3525*0Sstevel@tonic-gate 			tba = (struct T_bind_ack *)mp->b_rptr;
3526*0Sstevel@tonic-gate 			addrp = &mp->b_rptr[tba->ADDR_offset];
3527*0Sstevel@tonic-gate 			switch (icmp->icmp_family) {
3528*0Sstevel@tonic-gate 			case AF_INET:
3529*0Sstevel@tonic-gate 				if (tba->ADDR_length == sizeof (ipa_conn_t)) {
3530*0Sstevel@tonic-gate 					ac = (ipa_conn_t *)addrp;
3531*0Sstevel@tonic-gate 				} else {
3532*0Sstevel@tonic-gate 					ASSERT(tba->ADDR_length ==
3533*0Sstevel@tonic-gate 					    sizeof (ipa_conn_x_t));
3534*0Sstevel@tonic-gate 					ac = &((ipa_conn_x_t *)addrp)->acx_conn;
3535*0Sstevel@tonic-gate 				}
3536*0Sstevel@tonic-gate 				IN6_IPADDR_TO_V4MAPPED(ac->ac_laddr,
3537*0Sstevel@tonic-gate 				    &icmp->icmp_v6src);
3538*0Sstevel@tonic-gate 				break;
3539*0Sstevel@tonic-gate 			case AF_INET6:
3540*0Sstevel@tonic-gate 				if (tba->ADDR_length == sizeof (ipa6_conn_t)) {
3541*0Sstevel@tonic-gate 					ac6 = (ipa6_conn_t *)addrp;
3542*0Sstevel@tonic-gate 				} else {
3543*0Sstevel@tonic-gate 					ASSERT(tba->ADDR_length ==
3544*0Sstevel@tonic-gate 					    sizeof (ipa6_conn_x_t));
3545*0Sstevel@tonic-gate 					ac6 = &((ipa6_conn_x_t *)
3546*0Sstevel@tonic-gate 					    addrp)->ac6x_conn;
3547*0Sstevel@tonic-gate 				}
3548*0Sstevel@tonic-gate 				icmp->icmp_v6src = ac6->ac6_laddr;
3549*0Sstevel@tonic-gate 				(void) icmp_build_hdrs(q, icmp);
3550*0Sstevel@tonic-gate 			}
3551*0Sstevel@tonic-gate 		}
3552*0Sstevel@tonic-gate 		mp1 = mp1->b_cont;
3553*0Sstevel@tonic-gate 	}
3554*0Sstevel@tonic-gate 	/*
3555*0Sstevel@tonic-gate 	 * Look for one or more appended ACK message added by
3556*0Sstevel@tonic-gate 	 * icmp_connect or icmp_disconnect.
3557*0Sstevel@tonic-gate 	 * If none found just send up the T_BIND_ACK.
3558*0Sstevel@tonic-gate 	 * icmp_connect has appended a T_OK_ACK and a
3559*0Sstevel@tonic-gate 	 * T_CONN_CON.
3560*0Sstevel@tonic-gate 	 * icmp_disconnect has appended a T_OK_ACK.
3561*0Sstevel@tonic-gate 	 */
3562*0Sstevel@tonic-gate 	if (mp1 != NULL) {
3563*0Sstevel@tonic-gate 		if (mp->b_cont == mp1)
3564*0Sstevel@tonic-gate 			mp->b_cont = NULL;
3565*0Sstevel@tonic-gate 		else {
3566*0Sstevel@tonic-gate 			ASSERT(mp->b_cont->b_cont == mp1);
3567*0Sstevel@tonic-gate 			mp->b_cont->b_cont = NULL;
3568*0Sstevel@tonic-gate 		}
3569*0Sstevel@tonic-gate 		freemsg(mp);
3570*0Sstevel@tonic-gate 		mp = mp1;
3571*0Sstevel@tonic-gate 		while (mp != NULL) {
3572*0Sstevel@tonic-gate 			mp1 = mp->b_cont;
3573*0Sstevel@tonic-gate 			mp->b_cont = NULL;
3574*0Sstevel@tonic-gate 			putnext(q, mp);
3575*0Sstevel@tonic-gate 			mp = mp1;
3576*0Sstevel@tonic-gate 		}
3577*0Sstevel@tonic-gate 		return;
3578*0Sstevel@tonic-gate 	}
3579*0Sstevel@tonic-gate 	freemsg(mp->b_cont);
3580*0Sstevel@tonic-gate 	mp->b_cont = NULL;
3581*0Sstevel@tonic-gate 	putnext(q, mp);
3582*0Sstevel@tonic-gate }
3583*0Sstevel@tonic-gate 
3584*0Sstevel@tonic-gate /*
3585*0Sstevel@tonic-gate  * return SNMP stuff in buffer in mpdata
3586*0Sstevel@tonic-gate  */
3587*0Sstevel@tonic-gate static int
3588*0Sstevel@tonic-gate icmp_snmp_get(queue_t *q, mblk_t *mpctl)
3589*0Sstevel@tonic-gate {
3590*0Sstevel@tonic-gate 	mblk_t			*mpdata;
3591*0Sstevel@tonic-gate 	struct opthdr		*optp;
3592*0Sstevel@tonic-gate 
3593*0Sstevel@tonic-gate 	if (mpctl == NULL ||
3594*0Sstevel@tonic-gate 	    (mpdata = mpctl->b_cont) == NULL) {
3595*0Sstevel@tonic-gate 		return (0);
3596*0Sstevel@tonic-gate 	}
3597*0Sstevel@tonic-gate 
3598*0Sstevel@tonic-gate 	/* fixed length structure for IPv4 and IPv6 counters */
3599*0Sstevel@tonic-gate 	optp = (struct opthdr *)&mpctl->b_rptr[sizeof (struct T_optmgmt_ack)];
3600*0Sstevel@tonic-gate 	optp->level = EXPER_RAWIP;
3601*0Sstevel@tonic-gate 	optp->name = 0;
3602*0Sstevel@tonic-gate 	(void) snmp_append_data(mpdata, (char *)&rawip_mib, sizeof (rawip_mib));
3603*0Sstevel@tonic-gate 	optp->len = msgdsize(mpdata);
3604*0Sstevel@tonic-gate 	qreply(q, mpctl);
3605*0Sstevel@tonic-gate 
3606*0Sstevel@tonic-gate 	return (1);
3607*0Sstevel@tonic-gate }
3608*0Sstevel@tonic-gate 
3609*0Sstevel@tonic-gate /*
3610*0Sstevel@tonic-gate  * Return 0 if invalid set request, 1 otherwise, including non-rawip requests.
3611*0Sstevel@tonic-gate  * TODO:  If this ever actually tries to set anything, it needs to be
3612*0Sstevel@tonic-gate  * to do the appropriate locking.
3613*0Sstevel@tonic-gate  */
3614*0Sstevel@tonic-gate /* ARGSUSED */
3615*0Sstevel@tonic-gate static int
3616*0Sstevel@tonic-gate icmp_snmp_set(queue_t *q, t_scalar_t level, t_scalar_t name,
3617*0Sstevel@tonic-gate     uchar_t *ptr, int len)
3618*0Sstevel@tonic-gate {
3619*0Sstevel@tonic-gate 	switch (level) {
3620*0Sstevel@tonic-gate 	case EXPER_RAWIP:
3621*0Sstevel@tonic-gate 		return (0);
3622*0Sstevel@tonic-gate 	default:
3623*0Sstevel@tonic-gate 		return (1);
3624*0Sstevel@tonic-gate 	}
3625*0Sstevel@tonic-gate }
3626*0Sstevel@tonic-gate 
3627*0Sstevel@tonic-gate /* Report for ndd "icmp_status" */
3628*0Sstevel@tonic-gate /* ARGSUSED */
3629*0Sstevel@tonic-gate static int
3630*0Sstevel@tonic-gate icmp_status_report(queue_t *q, mblk_t *mp, caddr_t cp, cred_t *cr)
3631*0Sstevel@tonic-gate {
3632*0Sstevel@tonic-gate 	IDP	idp;
3633*0Sstevel@tonic-gate 	icmp_t	*icmp;
3634*0Sstevel@tonic-gate 	char	*state;
3635*0Sstevel@tonic-gate 	char	laddrbuf[INET6_ADDRSTRLEN];
3636*0Sstevel@tonic-gate 	char	faddrbuf[INET6_ADDRSTRLEN];
3637*0Sstevel@tonic-gate 
3638*0Sstevel@tonic-gate 	(void) mi_mpprintf(mp,
3639*0Sstevel@tonic-gate 	    "RAWIP    " MI_COL_HDRPAD_STR
3640*0Sstevel@tonic-gate 	/*   01234567[89ABCDEF] */
3641*0Sstevel@tonic-gate 	    "  src addr        dest addr       state");
3642*0Sstevel@tonic-gate 	/*   xxx.xxx.xxx.xxx xxx.xxx.xxx.xxx UNBOUND */
3643*0Sstevel@tonic-gate 
3644*0Sstevel@tonic-gate 
3645*0Sstevel@tonic-gate 	for (idp = mi_first_ptr(&icmp_g_head);
3646*0Sstevel@tonic-gate 	    (icmp = (icmp_t *)idp) != NULL;
3647*0Sstevel@tonic-gate 	    idp = mi_next_ptr(&icmp_g_head, idp)) {
3648*0Sstevel@tonic-gate 		if (icmp->icmp_state == TS_UNBND)
3649*0Sstevel@tonic-gate 			state = "UNBOUND";
3650*0Sstevel@tonic-gate 		else if (icmp->icmp_state == TS_IDLE)
3651*0Sstevel@tonic-gate 			state = "IDLE";
3652*0Sstevel@tonic-gate 		else if (icmp->icmp_state == TS_DATA_XFER)
3653*0Sstevel@tonic-gate 			state = "CONNECTED";
3654*0Sstevel@tonic-gate 		else
3655*0Sstevel@tonic-gate 			state = "UnkState";
3656*0Sstevel@tonic-gate 
3657*0Sstevel@tonic-gate 		(void) mi_mpprintf(mp,
3658*0Sstevel@tonic-gate 		    MI_COL_PTRFMT_STR "%s %s %s",
3659*0Sstevel@tonic-gate 		    (void *)icmp,
3660*0Sstevel@tonic-gate 		    inet_ntop(AF_INET6, &icmp->icmp_v6dst, faddrbuf,
3661*0Sstevel@tonic-gate 		    sizeof (faddrbuf)),
3662*0Sstevel@tonic-gate 		    inet_ntop(AF_INET6, &icmp->icmp_v6src, laddrbuf,
3663*0Sstevel@tonic-gate 		    sizeof (laddrbuf)),
3664*0Sstevel@tonic-gate 		    state);
3665*0Sstevel@tonic-gate 	}
3666*0Sstevel@tonic-gate 	return (0);
3667*0Sstevel@tonic-gate }
3668*0Sstevel@tonic-gate 
3669*0Sstevel@tonic-gate /*
3670*0Sstevel@tonic-gate  * This routine creates a T_UDERROR_IND message and passes it upstream.
3671*0Sstevel@tonic-gate  * The address and options are copied from the T_UNITDATA_REQ message
3672*0Sstevel@tonic-gate  * passed in mp.  This message is freed.
3673*0Sstevel@tonic-gate  */
3674*0Sstevel@tonic-gate static void
3675*0Sstevel@tonic-gate icmp_ud_err(queue_t *q, mblk_t *mp, t_scalar_t err)
3676*0Sstevel@tonic-gate {
3677*0Sstevel@tonic-gate 	mblk_t	*mp1;
3678*0Sstevel@tonic-gate 	uchar_t	*rptr = mp->b_rptr;
3679*0Sstevel@tonic-gate 	struct T_unitdata_req *tudr = (struct T_unitdata_req *)rptr;
3680*0Sstevel@tonic-gate 
3681*0Sstevel@tonic-gate 	mp1 = mi_tpi_uderror_ind((char *)&rptr[tudr->DEST_offset],
3682*0Sstevel@tonic-gate 	    tudr->DEST_length, (char *)&rptr[tudr->OPT_offset],
3683*0Sstevel@tonic-gate 	    tudr->OPT_length, err);
3684*0Sstevel@tonic-gate 	if (mp1)
3685*0Sstevel@tonic-gate 		qreply(q, mp1);
3686*0Sstevel@tonic-gate 	freemsg(mp);
3687*0Sstevel@tonic-gate }
3688*0Sstevel@tonic-gate 
3689*0Sstevel@tonic-gate /*
3690*0Sstevel@tonic-gate  * This routine is called by icmp_wput to handle T_UNBIND_REQ messages.
3691*0Sstevel@tonic-gate  * After some error checking, the message is passed downstream to ip.
3692*0Sstevel@tonic-gate  */
3693*0Sstevel@tonic-gate static void
3694*0Sstevel@tonic-gate icmp_unbind(queue_t *q, mblk_t *mp)
3695*0Sstevel@tonic-gate {
3696*0Sstevel@tonic-gate 	icmp_t	*icmp = (icmp_t *)q->q_ptr;
3697*0Sstevel@tonic-gate 
3698*0Sstevel@tonic-gate 	/* If a bind has not been done, we can't unbind. */
3699*0Sstevel@tonic-gate 	if (icmp->icmp_state == TS_UNBND) {
3700*0Sstevel@tonic-gate 		icmp_err_ack(q, mp, TOUTSTATE, 0);
3701*0Sstevel@tonic-gate 		return;
3702*0Sstevel@tonic-gate 	}
3703*0Sstevel@tonic-gate 	V6_SET_ZERO(icmp->icmp_v6src);
3704*0Sstevel@tonic-gate 	V6_SET_ZERO(icmp->icmp_bound_v6src);
3705*0Sstevel@tonic-gate 	icmp->icmp_state = TS_UNBND;
3706*0Sstevel@tonic-gate 
3707*0Sstevel@tonic-gate 	if (icmp->icmp_family == AF_INET6) {
3708*0Sstevel@tonic-gate 		int error;
3709*0Sstevel@tonic-gate 
3710*0Sstevel@tonic-gate 		/* Rebuild the header template */
3711*0Sstevel@tonic-gate 		error = icmp_build_hdrs(q, icmp);
3712*0Sstevel@tonic-gate 		if (error != 0) {
3713*0Sstevel@tonic-gate 			icmp_err_ack(q, mp, TSYSERR, error);
3714*0Sstevel@tonic-gate 			return;
3715*0Sstevel@tonic-gate 		}
3716*0Sstevel@tonic-gate 	}
3717*0Sstevel@tonic-gate 	/* Pass the unbind to IP. */
3718*0Sstevel@tonic-gate 	putnext(q, mp);
3719*0Sstevel@tonic-gate }
3720*0Sstevel@tonic-gate 
3721*0Sstevel@tonic-gate /*
3722*0Sstevel@tonic-gate  * Process IPv4 packets that already include an IP header.
3723*0Sstevel@tonic-gate  * Used when IP_HDRINCL has been set (implicit for IPPROTO_RAW and
3724*0Sstevel@tonic-gate  * IPPROTO_IGMP).
3725*0Sstevel@tonic-gate  */
3726*0Sstevel@tonic-gate static void
3727*0Sstevel@tonic-gate icmp_wput_hdrincl(queue_t *q, mblk_t *mp, icmp_t *icmp)
3728*0Sstevel@tonic-gate {
3729*0Sstevel@tonic-gate 	ipha_t	*ipha;
3730*0Sstevel@tonic-gate 	int	ip_hdr_length;
3731*0Sstevel@tonic-gate 	int	tp_hdr_len;
3732*0Sstevel@tonic-gate 	mblk_t	*mp1;
3733*0Sstevel@tonic-gate 	uint_t	pkt_len;
3734*0Sstevel@tonic-gate 
3735*0Sstevel@tonic-gate 	ipha = (ipha_t *)mp->b_rptr;
3736*0Sstevel@tonic-gate 	ip_hdr_length = IP_SIMPLE_HDR_LENGTH + icmp->icmp_ip_snd_options_len;
3737*0Sstevel@tonic-gate 	if ((mp->b_wptr - mp->b_rptr) < IP_SIMPLE_HDR_LENGTH) {
3738*0Sstevel@tonic-gate 		if (!pullupmsg(mp, IP_SIMPLE_HDR_LENGTH)) {
3739*0Sstevel@tonic-gate 			BUMP_MIB(&rawip_mib, rawipOutErrors);
3740*0Sstevel@tonic-gate 			freemsg(mp);
3741*0Sstevel@tonic-gate 			return;
3742*0Sstevel@tonic-gate 		}
3743*0Sstevel@tonic-gate 		ipha = (ipha_t *)mp->b_rptr;
3744*0Sstevel@tonic-gate 	}
3745*0Sstevel@tonic-gate 	ipha->ipha_version_and_hdr_length =
3746*0Sstevel@tonic-gate 	    (IP_VERSION<<4) | (ip_hdr_length>>2);
3747*0Sstevel@tonic-gate 
3748*0Sstevel@tonic-gate 	/*
3749*0Sstevel@tonic-gate 	 * For the socket of SOCK_RAW type, the checksum is provided in the
3750*0Sstevel@tonic-gate 	 * pre-built packet. We set the ipha_ident field to IP_HDR_INCLUDED to
3751*0Sstevel@tonic-gate 	 * tell IP that the application has sent a complete IP header and not
3752*0Sstevel@tonic-gate 	 * to compute the transport checksum nor change the DF flag.
3753*0Sstevel@tonic-gate 	 */
3754*0Sstevel@tonic-gate 	ipha->ipha_ident = IP_HDR_INCLUDED;
3755*0Sstevel@tonic-gate 	ipha->ipha_hdr_checksum = 0;
3756*0Sstevel@tonic-gate 	ipha->ipha_fragment_offset_and_flags &= htons(IPH_DF);
3757*0Sstevel@tonic-gate 	/* Insert options if any */
3758*0Sstevel@tonic-gate 	if (ip_hdr_length > IP_SIMPLE_HDR_LENGTH) {
3759*0Sstevel@tonic-gate 		/*
3760*0Sstevel@tonic-gate 		 * Put the IP header plus any transport header that is
3761*0Sstevel@tonic-gate 		 * checksumed by ip_wput into the first mblk. (ip_wput assumes
3762*0Sstevel@tonic-gate 		 * that at least the checksum field is in the first mblk.)
3763*0Sstevel@tonic-gate 		 */
3764*0Sstevel@tonic-gate 		switch (ipha->ipha_protocol) {
3765*0Sstevel@tonic-gate 		case IPPROTO_UDP:
3766*0Sstevel@tonic-gate 			tp_hdr_len = 8;
3767*0Sstevel@tonic-gate 			break;
3768*0Sstevel@tonic-gate 		case IPPROTO_TCP:
3769*0Sstevel@tonic-gate 			tp_hdr_len = 20;
3770*0Sstevel@tonic-gate 			break;
3771*0Sstevel@tonic-gate 		default:
3772*0Sstevel@tonic-gate 			tp_hdr_len = 0;
3773*0Sstevel@tonic-gate 			break;
3774*0Sstevel@tonic-gate 		}
3775*0Sstevel@tonic-gate 		/*
3776*0Sstevel@tonic-gate 		 * The code below assumes that IP_SIMPLE_HDR_LENGTH plus
3777*0Sstevel@tonic-gate 		 * tp_hdr_len bytes will be in a single mblk.
3778*0Sstevel@tonic-gate 		 */
3779*0Sstevel@tonic-gate 		if ((mp->b_wptr - mp->b_rptr) < (IP_SIMPLE_HDR_LENGTH +
3780*0Sstevel@tonic-gate 		    tp_hdr_len)) {
3781*0Sstevel@tonic-gate 			if (!pullupmsg(mp, IP_SIMPLE_HDR_LENGTH +
3782*0Sstevel@tonic-gate 			    tp_hdr_len)) {
3783*0Sstevel@tonic-gate 				BUMP_MIB(&rawip_mib, rawipOutErrors);
3784*0Sstevel@tonic-gate 				freemsg(mp);
3785*0Sstevel@tonic-gate 				return;
3786*0Sstevel@tonic-gate 			}
3787*0Sstevel@tonic-gate 			ipha = (ipha_t *)mp->b_rptr;
3788*0Sstevel@tonic-gate 		}
3789*0Sstevel@tonic-gate 
3790*0Sstevel@tonic-gate 		/*
3791*0Sstevel@tonic-gate 		 * if the length is larger then the max allowed IP packet,
3792*0Sstevel@tonic-gate 		 * then send an error and abort the processing.
3793*0Sstevel@tonic-gate 		 */
3794*0Sstevel@tonic-gate 		pkt_len = ntohs(ipha->ipha_length)
3795*0Sstevel@tonic-gate 		    + icmp->icmp_ip_snd_options_len;
3796*0Sstevel@tonic-gate 		if (pkt_len > IP_MAXPACKET) {
3797*0Sstevel@tonic-gate 			icmp_ud_err(q, mp, EMSGSIZE);
3798*0Sstevel@tonic-gate 			return;
3799*0Sstevel@tonic-gate 		}
3800*0Sstevel@tonic-gate 		if (!(mp1 = allocb(ip_hdr_length + icmp_wroff_extra +
3801*0Sstevel@tonic-gate 		    tp_hdr_len, BPRI_LO))) {
3802*0Sstevel@tonic-gate 			icmp_ud_err(q, mp, ENOMEM);
3803*0Sstevel@tonic-gate 			return;
3804*0Sstevel@tonic-gate 		}
3805*0Sstevel@tonic-gate 		mp1->b_rptr += icmp_wroff_extra;
3806*0Sstevel@tonic-gate 		mp1->b_wptr = mp1->b_rptr + ip_hdr_length;
3807*0Sstevel@tonic-gate 
3808*0Sstevel@tonic-gate 		ipha->ipha_length = htons((uint16_t)pkt_len);
3809*0Sstevel@tonic-gate 		bcopy(ipha, mp1->b_rptr, IP_SIMPLE_HDR_LENGTH);
3810*0Sstevel@tonic-gate 
3811*0Sstevel@tonic-gate 		/* Copy transport header if any */
3812*0Sstevel@tonic-gate 		bcopy(&ipha[1], mp1->b_wptr, tp_hdr_len);
3813*0Sstevel@tonic-gate 		mp1->b_wptr += tp_hdr_len;
3814*0Sstevel@tonic-gate 
3815*0Sstevel@tonic-gate 		/* Add options */
3816*0Sstevel@tonic-gate 		ipha = (ipha_t *)mp1->b_rptr;
3817*0Sstevel@tonic-gate 		bcopy(icmp->icmp_ip_snd_options, &ipha[1],
3818*0Sstevel@tonic-gate 		    icmp->icmp_ip_snd_options_len);
3819*0Sstevel@tonic-gate 
3820*0Sstevel@tonic-gate 		/* Drop IP header and transport header from original */
3821*0Sstevel@tonic-gate 		(void) adjmsg(mp, IP_SIMPLE_HDR_LENGTH + tp_hdr_len);
3822*0Sstevel@tonic-gate 
3823*0Sstevel@tonic-gate 		mp1->b_cont = mp;
3824*0Sstevel@tonic-gate 		mp = mp1;
3825*0Sstevel@tonic-gate 		/*
3826*0Sstevel@tonic-gate 		 * Massage source route putting first source
3827*0Sstevel@tonic-gate 		 * route in ipha_dst.
3828*0Sstevel@tonic-gate 		 */
3829*0Sstevel@tonic-gate 		(void) ip_massage_options(ipha);
3830*0Sstevel@tonic-gate 	}
3831*0Sstevel@tonic-gate 	putnext(q, mp);
3832*0Sstevel@tonic-gate }
3833*0Sstevel@tonic-gate 
3834*0Sstevel@tonic-gate /*
3835*0Sstevel@tonic-gate  * This routine handles all messages passed downstream.  It either
3836*0Sstevel@tonic-gate  * consumes the message or passes it downstream; it never queues a
3837*0Sstevel@tonic-gate  * a message.
3838*0Sstevel@tonic-gate  */
3839*0Sstevel@tonic-gate static void
3840*0Sstevel@tonic-gate icmp_wput(queue_t *q, mblk_t *mp)
3841*0Sstevel@tonic-gate {
3842*0Sstevel@tonic-gate 	uchar_t	*rptr = mp->b_rptr;
3843*0Sstevel@tonic-gate 	ipha_t	*ipha;
3844*0Sstevel@tonic-gate 	mblk_t	*mp1;
3845*0Sstevel@tonic-gate 	int	ip_hdr_length;
3846*0Sstevel@tonic-gate #define	tudr ((struct T_unitdata_req *)rptr)
3847*0Sstevel@tonic-gate 	size_t	ip_len;
3848*0Sstevel@tonic-gate 	icmp_t	*icmp;
3849*0Sstevel@tonic-gate 	sin6_t	*sin6;
3850*0Sstevel@tonic-gate 	sin_t	*sin;
3851*0Sstevel@tonic-gate 	ipaddr_t	v4dst;
3852*0Sstevel@tonic-gate 
3853*0Sstevel@tonic-gate 	icmp = (icmp_t *)q->q_ptr;
3854*0Sstevel@tonic-gate 	if (icmp->icmp_restricted) {
3855*0Sstevel@tonic-gate 		icmp_wput_restricted(q, mp);
3856*0Sstevel@tonic-gate 		return;
3857*0Sstevel@tonic-gate 	}
3858*0Sstevel@tonic-gate 
3859*0Sstevel@tonic-gate 	switch (mp->b_datap->db_type) {
3860*0Sstevel@tonic-gate 	case M_DATA:
3861*0Sstevel@tonic-gate 		if (icmp->icmp_hdrincl) {
3862*0Sstevel@tonic-gate 			ASSERT(icmp->icmp_ipversion == IPV4_VERSION);
3863*0Sstevel@tonic-gate 			icmp_wput_hdrincl(q, mp, icmp);
3864*0Sstevel@tonic-gate 			return;
3865*0Sstevel@tonic-gate 		}
3866*0Sstevel@tonic-gate 		freemsg(mp);
3867*0Sstevel@tonic-gate 		return;
3868*0Sstevel@tonic-gate 	case M_PROTO:
3869*0Sstevel@tonic-gate 	case M_PCPROTO:
3870*0Sstevel@tonic-gate 		ip_len = mp->b_wptr - rptr;
3871*0Sstevel@tonic-gate 		if (ip_len >= sizeof (struct T_unitdata_req)) {
3872*0Sstevel@tonic-gate 			/* Expedite valid T_UNITDATA_REQ to below the switch */
3873*0Sstevel@tonic-gate 			if (((union T_primitives *)rptr)->type
3874*0Sstevel@tonic-gate 			    == T_UNITDATA_REQ)
3875*0Sstevel@tonic-gate 				break;
3876*0Sstevel@tonic-gate 		}
3877*0Sstevel@tonic-gate 		/* FALLTHRU */
3878*0Sstevel@tonic-gate 	default:
3879*0Sstevel@tonic-gate 		icmp_wput_other(q, mp);
3880*0Sstevel@tonic-gate 		return;
3881*0Sstevel@tonic-gate 	}
3882*0Sstevel@tonic-gate 
3883*0Sstevel@tonic-gate 	/* Handle T_UNITDATA_REQ messages here. */
3884*0Sstevel@tonic-gate 
3885*0Sstevel@tonic-gate 	if (icmp->icmp_state == TS_UNBND) {
3886*0Sstevel@tonic-gate 		/* If a port has not been bound to the stream, fail. */
3887*0Sstevel@tonic-gate 		BUMP_MIB(&rawip_mib, rawipOutErrors);
3888*0Sstevel@tonic-gate 		icmp_ud_err(q, mp, EPROTO);
3889*0Sstevel@tonic-gate 		return;
3890*0Sstevel@tonic-gate 	}
3891*0Sstevel@tonic-gate 	mp1 = mp->b_cont;
3892*0Sstevel@tonic-gate 	if (mp1 == NULL) {
3893*0Sstevel@tonic-gate 		BUMP_MIB(&rawip_mib, rawipOutErrors);
3894*0Sstevel@tonic-gate 		icmp_ud_err(q, mp, EPROTO);
3895*0Sstevel@tonic-gate 		return;
3896*0Sstevel@tonic-gate 	}
3897*0Sstevel@tonic-gate 
3898*0Sstevel@tonic-gate 	if ((rptr + tudr->DEST_offset + tudr->DEST_length) > mp->b_wptr) {
3899*0Sstevel@tonic-gate 		BUMP_MIB(&rawip_mib, rawipOutErrors);
3900*0Sstevel@tonic-gate 		icmp_ud_err(q, mp, EADDRNOTAVAIL);
3901*0Sstevel@tonic-gate 		return;
3902*0Sstevel@tonic-gate 	}
3903*0Sstevel@tonic-gate 
3904*0Sstevel@tonic-gate 	switch (icmp->icmp_family) {
3905*0Sstevel@tonic-gate 	case AF_INET6:
3906*0Sstevel@tonic-gate 		sin6 = (sin6_t *)&rptr[tudr->DEST_offset];
3907*0Sstevel@tonic-gate 		if (!OK_32PTR((char *)sin6) ||
3908*0Sstevel@tonic-gate 		    tudr->DEST_length != sizeof (sin6_t) ||
3909*0Sstevel@tonic-gate 		    sin6->sin6_family != AF_INET6) {
3910*0Sstevel@tonic-gate 			BUMP_MIB(&rawip_mib, rawipOutErrors);
3911*0Sstevel@tonic-gate 			icmp_ud_err(q, mp, EADDRNOTAVAIL);
3912*0Sstevel@tonic-gate 			return;
3913*0Sstevel@tonic-gate 		}
3914*0Sstevel@tonic-gate 
3915*0Sstevel@tonic-gate 		/* No support for mapped addresses on raw sockets */
3916*0Sstevel@tonic-gate 		if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
3917*0Sstevel@tonic-gate 			BUMP_MIB(&rawip_mib, rawipOutErrors);
3918*0Sstevel@tonic-gate 			icmp_ud_err(q, mp, EADDRNOTAVAIL);
3919*0Sstevel@tonic-gate 			return;
3920*0Sstevel@tonic-gate 		}
3921*0Sstevel@tonic-gate 
3922*0Sstevel@tonic-gate 		/*
3923*0Sstevel@tonic-gate 		 * Destination is a native IPv6 address.
3924*0Sstevel@tonic-gate 		 * Send out an IPv6 format packet.
3925*0Sstevel@tonic-gate 		 */
3926*0Sstevel@tonic-gate 		icmp_wput_ipv6(q, mp, sin6, tudr->OPT_length);
3927*0Sstevel@tonic-gate 		return;
3928*0Sstevel@tonic-gate 
3929*0Sstevel@tonic-gate 	case AF_INET:
3930*0Sstevel@tonic-gate 		sin = (sin_t *)&rptr[tudr->DEST_offset];
3931*0Sstevel@tonic-gate 		if (!OK_32PTR((char *)sin) ||
3932*0Sstevel@tonic-gate 		    tudr->DEST_length != sizeof (sin_t) ||
3933*0Sstevel@tonic-gate 		    sin->sin_family != AF_INET) {
3934*0Sstevel@tonic-gate 			BUMP_MIB(&rawip_mib, rawipOutErrors);
3935*0Sstevel@tonic-gate 			icmp_ud_err(q, mp, EADDRNOTAVAIL);
3936*0Sstevel@tonic-gate 			return;
3937*0Sstevel@tonic-gate 		}
3938*0Sstevel@tonic-gate 		/* Extract and ipaddr */
3939*0Sstevel@tonic-gate 		v4dst = sin->sin_addr.s_addr;
3940*0Sstevel@tonic-gate 		break;
3941*0Sstevel@tonic-gate 	}
3942*0Sstevel@tonic-gate 
3943*0Sstevel@tonic-gate 	/*
3944*0Sstevel@tonic-gate 	 * If options passed in, feed it for verification and handling
3945*0Sstevel@tonic-gate 	 */
3946*0Sstevel@tonic-gate 	if (tudr->OPT_length != 0) {
3947*0Sstevel@tonic-gate 		int error;
3948*0Sstevel@tonic-gate 
3949*0Sstevel@tonic-gate 		if (icmp_unitdata_opt_process(q, mp, &error,
3950*0Sstevel@tonic-gate 		    (uchar_t *)0) < 0) {
3951*0Sstevel@tonic-gate 			/* failure */
3952*0Sstevel@tonic-gate 			BUMP_MIB(&rawip_mib, rawipOutErrors);
3953*0Sstevel@tonic-gate 			icmp_ud_err(q, mp, error);
3954*0Sstevel@tonic-gate 			return;
3955*0Sstevel@tonic-gate 		}
3956*0Sstevel@tonic-gate 		/*
3957*0Sstevel@tonic-gate 		 * Note: Success in processing options.
3958*0Sstevel@tonic-gate 		 * mp option buffer represented by
3959*0Sstevel@tonic-gate 		 * OPT_length/offset now potentially modified
3960*0Sstevel@tonic-gate 		 * and contain option setting results
3961*0Sstevel@tonic-gate 		 */
3962*0Sstevel@tonic-gate 	}
3963*0Sstevel@tonic-gate 
3964*0Sstevel@tonic-gate 	/* Protocol 255 contains full IP headers */
3965*0Sstevel@tonic-gate 	if (icmp->icmp_hdrincl) {
3966*0Sstevel@tonic-gate 		freeb(mp);
3967*0Sstevel@tonic-gate 		icmp_wput_hdrincl(q, mp1, icmp);
3968*0Sstevel@tonic-gate 		return;
3969*0Sstevel@tonic-gate 	}
3970*0Sstevel@tonic-gate 	/* Add an IP header */
3971*0Sstevel@tonic-gate 	ip_hdr_length = IP_SIMPLE_HDR_LENGTH + icmp->icmp_ip_snd_options_len;
3972*0Sstevel@tonic-gate 	ipha = (ipha_t *)&mp1->b_rptr[-ip_hdr_length];
3973*0Sstevel@tonic-gate 	if ((uchar_t *)ipha < mp1->b_datap->db_base ||
3974*0Sstevel@tonic-gate 	    mp1->b_datap->db_ref != 1 ||
3975*0Sstevel@tonic-gate 	    !OK_32PTR(ipha)) {
3976*0Sstevel@tonic-gate 		if (!(mp1 = allocb(ip_hdr_length + icmp_wroff_extra,
3977*0Sstevel@tonic-gate 		    BPRI_LO))) {
3978*0Sstevel@tonic-gate 			BUMP_MIB(&rawip_mib, rawipOutErrors);
3979*0Sstevel@tonic-gate 			icmp_ud_err(q, mp1, ENOMEM);
3980*0Sstevel@tonic-gate 			return;
3981*0Sstevel@tonic-gate 		}
3982*0Sstevel@tonic-gate 		mp1->b_cont = mp->b_cont;
3983*0Sstevel@tonic-gate 		ipha = (ipha_t *)mp1->b_datap->db_lim;
3984*0Sstevel@tonic-gate 		mp1->b_wptr = (uchar_t *)ipha;
3985*0Sstevel@tonic-gate 		ipha = (ipha_t *)((uchar_t *)ipha - ip_hdr_length);
3986*0Sstevel@tonic-gate 	}
3987*0Sstevel@tonic-gate #ifdef	_BIG_ENDIAN
3988*0Sstevel@tonic-gate 	/* Set version, header length, and tos */
3989*0Sstevel@tonic-gate 	*(uint16_t *)&ipha->ipha_version_and_hdr_length =
3990*0Sstevel@tonic-gate 	    ((((IP_VERSION << 4) | (ip_hdr_length>>2)) << 8) |
3991*0Sstevel@tonic-gate 		icmp->icmp_type_of_service);
3992*0Sstevel@tonic-gate 	/* Set ttl and protocol */
3993*0Sstevel@tonic-gate 	*(uint16_t *)&ipha->ipha_ttl = (icmp->icmp_ttl << 8) | icmp->icmp_proto;
3994*0Sstevel@tonic-gate #else
3995*0Sstevel@tonic-gate 	/* Set version, header length, and tos */
3996*0Sstevel@tonic-gate 	*(uint16_t *)&ipha->ipha_version_and_hdr_length =
3997*0Sstevel@tonic-gate 	    ((icmp->icmp_type_of_service << 8) |
3998*0Sstevel@tonic-gate 		((IP_VERSION << 4) | (ip_hdr_length>>2)));
3999*0Sstevel@tonic-gate 	/* Set ttl and protocol */
4000*0Sstevel@tonic-gate 	*(uint16_t *)&ipha->ipha_ttl = (icmp->icmp_proto << 8) | icmp->icmp_ttl;
4001*0Sstevel@tonic-gate #endif
4002*0Sstevel@tonic-gate 	/*
4003*0Sstevel@tonic-gate 	 * Copy our address into the packet.  If this is zero,
4004*0Sstevel@tonic-gate 	 * ip will fill in the real source address.
4005*0Sstevel@tonic-gate 	 */
4006*0Sstevel@tonic-gate 	IN6_V4MAPPED_TO_IPADDR(&icmp->icmp_v6src, ipha->ipha_src);
4007*0Sstevel@tonic-gate 	ipha->ipha_fragment_offset_and_flags = 0;
4008*0Sstevel@tonic-gate 
4009*0Sstevel@tonic-gate 	/*
4010*0Sstevel@tonic-gate 	 * For the socket of SOCK_RAW type, the checksum is provided in the
4011*0Sstevel@tonic-gate 	 * pre-built packet. We set the ipha_ident field to IP_HDR_INCLUDED to
4012*0Sstevel@tonic-gate 	 * tell IP that the application has sent a complete IP header and not
4013*0Sstevel@tonic-gate 	 * to compute the transport checksum nor change the DF flag.
4014*0Sstevel@tonic-gate 	 */
4015*0Sstevel@tonic-gate 	ipha->ipha_ident = IP_HDR_INCLUDED;
4016*0Sstevel@tonic-gate 
4017*0Sstevel@tonic-gate 	/* Finish common formatting of the packet. */
4018*0Sstevel@tonic-gate 	mp1->b_rptr = (uchar_t *)ipha;
4019*0Sstevel@tonic-gate 
4020*0Sstevel@tonic-gate 	ip_len = mp1->b_wptr - (uchar_t *)ipha;
4021*0Sstevel@tonic-gate 	if (mp1->b_cont != NULL)
4022*0Sstevel@tonic-gate 		ip_len += msgdsize(mp1->b_cont);
4023*0Sstevel@tonic-gate 
4024*0Sstevel@tonic-gate 	/*
4025*0Sstevel@tonic-gate 	 * Set the length into the IP header.
4026*0Sstevel@tonic-gate 	 * If the length is greater than the maximum allowed by IP,
4027*0Sstevel@tonic-gate 	 * then free the message and return. Do not try and send it
4028*0Sstevel@tonic-gate 	 * as this can cause problems in layers below.
4029*0Sstevel@tonic-gate 	 */
4030*0Sstevel@tonic-gate 	if (ip_len > IP_MAXPACKET) {
4031*0Sstevel@tonic-gate 		BUMP_MIB(&rawip_mib, rawipOutErrors);
4032*0Sstevel@tonic-gate 		icmp_ud_err(q, mp, EMSGSIZE);
4033*0Sstevel@tonic-gate 		return;
4034*0Sstevel@tonic-gate 	}
4035*0Sstevel@tonic-gate 	ipha->ipha_length = htons((uint16_t)ip_len);
4036*0Sstevel@tonic-gate 	/*
4037*0Sstevel@tonic-gate 	 * Copy in the destination address from the T_UNITDATA
4038*0Sstevel@tonic-gate 	 * request
4039*0Sstevel@tonic-gate 	 */
4040*0Sstevel@tonic-gate 	if (v4dst == INADDR_ANY)
4041*0Sstevel@tonic-gate 		ipha->ipha_dst = htonl(INADDR_LOOPBACK);
4042*0Sstevel@tonic-gate 	else
4043*0Sstevel@tonic-gate 		ipha->ipha_dst = v4dst;
4044*0Sstevel@tonic-gate 
4045*0Sstevel@tonic-gate 	/*
4046*0Sstevel@tonic-gate 	 * Set ttl based on IP_MULTICAST_TTL to match IPv6 logic.
4047*0Sstevel@tonic-gate 	 */
4048*0Sstevel@tonic-gate 	if (CLASSD(v4dst))
4049*0Sstevel@tonic-gate 		ipha->ipha_ttl = icmp->icmp_multicast_ttl;
4050*0Sstevel@tonic-gate 
4051*0Sstevel@tonic-gate 	/* Copy in options if any */
4052*0Sstevel@tonic-gate 	if (ip_hdr_length > IP_SIMPLE_HDR_LENGTH) {
4053*0Sstevel@tonic-gate 		bcopy(icmp->icmp_ip_snd_options,
4054*0Sstevel@tonic-gate 		    &ipha[1], icmp->icmp_ip_snd_options_len);
4055*0Sstevel@tonic-gate 		/*
4056*0Sstevel@tonic-gate 		 * Massage source route putting first source route in ipha_dst.
4057*0Sstevel@tonic-gate 		 * Ignore the destination in the T_unitdata_req.
4058*0Sstevel@tonic-gate 		 */
4059*0Sstevel@tonic-gate 		(void) ip_massage_options(ipha);
4060*0Sstevel@tonic-gate 	}
4061*0Sstevel@tonic-gate 	freeb(mp);
4062*0Sstevel@tonic-gate 	BUMP_MIB(&rawip_mib, rawipOutDatagrams);
4063*0Sstevel@tonic-gate 	putnext(q, mp1);
4064*0Sstevel@tonic-gate #undef	ipha
4065*0Sstevel@tonic-gate #undef tudr
4066*0Sstevel@tonic-gate }
4067*0Sstevel@tonic-gate 
4068*0Sstevel@tonic-gate /*
4069*0Sstevel@tonic-gate  * icmp_wput_ipv6():
4070*0Sstevel@tonic-gate  * Assumes that icmp_wput did some sanity checking on the destination
4071*0Sstevel@tonic-gate  * address.
4072*0Sstevel@tonic-gate  */
4073*0Sstevel@tonic-gate void
4074*0Sstevel@tonic-gate icmp_wput_ipv6(queue_t *q, mblk_t *mp, sin6_t *sin6, t_scalar_t tudr_optlen)
4075*0Sstevel@tonic-gate {
4076*0Sstevel@tonic-gate 	ip6_t			*ip6h;
4077*0Sstevel@tonic-gate 	ip6i_t			*ip6i;	/* mp1->b_rptr even if no ip6i_t */
4078*0Sstevel@tonic-gate 	mblk_t			*mp1;
4079*0Sstevel@tonic-gate 	int			ip_hdr_len = IPV6_HDR_LEN;
4080*0Sstevel@tonic-gate 	size_t			ip_len;
4081*0Sstevel@tonic-gate 	icmp_t			*icmp;
4082*0Sstevel@tonic-gate 	ip6_pkt_t		ipp_s;	/* For ancillary data options */
4083*0Sstevel@tonic-gate 	ip6_pkt_t		*ipp = &ipp_s;
4084*0Sstevel@tonic-gate 	ip6_pkt_t		*tipp;
4085*0Sstevel@tonic-gate 	uint32_t		csum = 0;
4086*0Sstevel@tonic-gate 	uint_t			ignore = 0;
4087*0Sstevel@tonic-gate 	uint_t			option_exists = 0, is_sticky = 0;
4088*0Sstevel@tonic-gate 	uint8_t			*cp;
4089*0Sstevel@tonic-gate 	uint8_t			*nxthdr_ptr;
4090*0Sstevel@tonic-gate 
4091*0Sstevel@tonic-gate 	icmp = (icmp_t *)q->q_ptr;
4092*0Sstevel@tonic-gate 
4093*0Sstevel@tonic-gate 	/*
4094*0Sstevel@tonic-gate 	 * If the local address is a mapped address return
4095*0Sstevel@tonic-gate 	 * an error.
4096*0Sstevel@tonic-gate 	 * It would be possible to send an IPv6 packet but the
4097*0Sstevel@tonic-gate 	 * response would never make it back to the application
4098*0Sstevel@tonic-gate 	 * since it is bound to a mapped address.
4099*0Sstevel@tonic-gate 	 */
4100*0Sstevel@tonic-gate 	if (IN6_IS_ADDR_V4MAPPED(&icmp->icmp_v6src)) {
4101*0Sstevel@tonic-gate 		BUMP_MIB(&rawip_mib, rawipOutErrors);
4102*0Sstevel@tonic-gate 		icmp_ud_err(q, mp, EADDRNOTAVAIL);
4103*0Sstevel@tonic-gate 		return;
4104*0Sstevel@tonic-gate 	}
4105*0Sstevel@tonic-gate 
4106*0Sstevel@tonic-gate 	ipp->ipp_fields = 0;
4107*0Sstevel@tonic-gate 	ipp->ipp_sticky_ignored = 0;
4108*0Sstevel@tonic-gate 
4109*0Sstevel@tonic-gate 	/*
4110*0Sstevel@tonic-gate 	 * If TPI options passed in, feed it for verification and handling
4111*0Sstevel@tonic-gate 	 */
4112*0Sstevel@tonic-gate 	if (tudr_optlen != 0) {
4113*0Sstevel@tonic-gate 		int error;
4114*0Sstevel@tonic-gate 
4115*0Sstevel@tonic-gate 		if (icmp_unitdata_opt_process(q, mp, &error,
4116*0Sstevel@tonic-gate 		    (void *)ipp) < 0) {
4117*0Sstevel@tonic-gate 			/* failure */
4118*0Sstevel@tonic-gate 			BUMP_MIB(&rawip_mib, rawipOutErrors);
4119*0Sstevel@tonic-gate 			icmp_ud_err(q, mp, error);
4120*0Sstevel@tonic-gate 			return;
4121*0Sstevel@tonic-gate 		}
4122*0Sstevel@tonic-gate 		ignore = ipp->ipp_sticky_ignored;
4123*0Sstevel@tonic-gate 		ASSERT(error == 0);
4124*0Sstevel@tonic-gate 	}
4125*0Sstevel@tonic-gate 
4126*0Sstevel@tonic-gate 	if (sin6->sin6_scope_id != 0 &&
4127*0Sstevel@tonic-gate 	    IN6_IS_ADDR_LINKSCOPE(&sin6->sin6_addr)) {
4128*0Sstevel@tonic-gate 		/*
4129*0Sstevel@tonic-gate 		 * IPPF_SCOPE_ID is special.  It's neither a sticky
4130*0Sstevel@tonic-gate 		 * option nor ancillary data.  It needs to be
4131*0Sstevel@tonic-gate 		 * explicitly set in options_exists.
4132*0Sstevel@tonic-gate 		 */
4133*0Sstevel@tonic-gate 		option_exists |= IPPF_SCOPE_ID;
4134*0Sstevel@tonic-gate 	}
4135*0Sstevel@tonic-gate 
4136*0Sstevel@tonic-gate 	if ((icmp->icmp_sticky_ipp.ipp_fields == 0) &&
4137*0Sstevel@tonic-gate 	    (ipp->ipp_fields == 0)) {
4138*0Sstevel@tonic-gate 		/* No sticky options nor ancillary data. */
4139*0Sstevel@tonic-gate 		goto no_options;
4140*0Sstevel@tonic-gate 	}
4141*0Sstevel@tonic-gate 
4142*0Sstevel@tonic-gate 	/*
4143*0Sstevel@tonic-gate 	 * Go through the options figuring out where each is going to
4144*0Sstevel@tonic-gate 	 * come from and build two masks.  The first mask indicates if
4145*0Sstevel@tonic-gate 	 * the option exists at all.  The second mask indicates if the
4146*0Sstevel@tonic-gate 	 * option is sticky or ancillary.
4147*0Sstevel@tonic-gate 	 */
4148*0Sstevel@tonic-gate 	if (!(ignore & IPPF_HOPOPTS)) {
4149*0Sstevel@tonic-gate 		if (ipp->ipp_fields & IPPF_HOPOPTS) {
4150*0Sstevel@tonic-gate 			option_exists |= IPPF_HOPOPTS;
4151*0Sstevel@tonic-gate 			ip_hdr_len += ipp->ipp_hopoptslen;
4152*0Sstevel@tonic-gate 		} else if (icmp->icmp_sticky_ipp.ipp_fields & IPPF_HOPOPTS) {
4153*0Sstevel@tonic-gate 			option_exists |= IPPF_HOPOPTS;
4154*0Sstevel@tonic-gate 			is_sticky |= IPPF_HOPOPTS;
4155*0Sstevel@tonic-gate 			ip_hdr_len += icmp->icmp_sticky_ipp.ipp_hopoptslen;
4156*0Sstevel@tonic-gate 		}
4157*0Sstevel@tonic-gate 	}
4158*0Sstevel@tonic-gate 
4159*0Sstevel@tonic-gate 	if (!(ignore & IPPF_RTHDR)) {
4160*0Sstevel@tonic-gate 		if (ipp->ipp_fields & IPPF_RTHDR) {
4161*0Sstevel@tonic-gate 			option_exists |= IPPF_RTHDR;
4162*0Sstevel@tonic-gate 			ip_hdr_len += ipp->ipp_rthdrlen;
4163*0Sstevel@tonic-gate 		} else if (icmp->icmp_sticky_ipp.ipp_fields & IPPF_RTHDR) {
4164*0Sstevel@tonic-gate 			option_exists |= IPPF_RTHDR;
4165*0Sstevel@tonic-gate 			is_sticky |= IPPF_RTHDR;
4166*0Sstevel@tonic-gate 			ip_hdr_len += icmp->icmp_sticky_ipp.ipp_rthdrlen;
4167*0Sstevel@tonic-gate 		}
4168*0Sstevel@tonic-gate 	}
4169*0Sstevel@tonic-gate 
4170*0Sstevel@tonic-gate 	if (!(ignore & IPPF_RTDSTOPTS) && (option_exists & IPPF_RTHDR)) {
4171*0Sstevel@tonic-gate 		/*
4172*0Sstevel@tonic-gate 		 * Need to have a router header to use these.
4173*0Sstevel@tonic-gate 		 */
4174*0Sstevel@tonic-gate 		if (ipp->ipp_fields & IPPF_RTDSTOPTS) {
4175*0Sstevel@tonic-gate 			option_exists |= IPPF_RTDSTOPTS;
4176*0Sstevel@tonic-gate 			ip_hdr_len += ipp->ipp_rtdstoptslen;
4177*0Sstevel@tonic-gate 		} else if (icmp->icmp_sticky_ipp.ipp_fields & IPPF_RTDSTOPTS) {
4178*0Sstevel@tonic-gate 			option_exists |= IPPF_RTDSTOPTS;
4179*0Sstevel@tonic-gate 			is_sticky |= IPPF_RTDSTOPTS;
4180*0Sstevel@tonic-gate 			ip_hdr_len +=
4181*0Sstevel@tonic-gate 			    icmp->icmp_sticky_ipp.ipp_rtdstoptslen;
4182*0Sstevel@tonic-gate 		}
4183*0Sstevel@tonic-gate 	}
4184*0Sstevel@tonic-gate 
4185*0Sstevel@tonic-gate 	if (!(ignore & IPPF_DSTOPTS)) {
4186*0Sstevel@tonic-gate 		if (ipp->ipp_fields & IPPF_DSTOPTS) {
4187*0Sstevel@tonic-gate 			option_exists |= IPPF_DSTOPTS;
4188*0Sstevel@tonic-gate 			ip_hdr_len += ipp->ipp_dstoptslen;
4189*0Sstevel@tonic-gate 		} else if (icmp->icmp_sticky_ipp.ipp_fields & IPPF_DSTOPTS) {
4190*0Sstevel@tonic-gate 			option_exists |= IPPF_DSTOPTS;
4191*0Sstevel@tonic-gate 			is_sticky |= IPPF_DSTOPTS;
4192*0Sstevel@tonic-gate 			ip_hdr_len += icmp->icmp_sticky_ipp.ipp_dstoptslen;
4193*0Sstevel@tonic-gate 		}
4194*0Sstevel@tonic-gate 	}
4195*0Sstevel@tonic-gate 
4196*0Sstevel@tonic-gate 	if (!(ignore & IPPF_IFINDEX)) {
4197*0Sstevel@tonic-gate 		if (ipp->ipp_fields & IPPF_IFINDEX) {
4198*0Sstevel@tonic-gate 			option_exists |= IPPF_IFINDEX;
4199*0Sstevel@tonic-gate 		} else if (icmp->icmp_sticky_ipp.ipp_fields & IPPF_IFINDEX) {
4200*0Sstevel@tonic-gate 			option_exists |= IPPF_IFINDEX;
4201*0Sstevel@tonic-gate 			is_sticky |= IPPF_IFINDEX;
4202*0Sstevel@tonic-gate 		}
4203*0Sstevel@tonic-gate 	}
4204*0Sstevel@tonic-gate 
4205*0Sstevel@tonic-gate 	if (!(ignore & IPPF_ADDR)) {
4206*0Sstevel@tonic-gate 		if (ipp->ipp_fields & IPPF_ADDR) {
4207*0Sstevel@tonic-gate 			option_exists |= IPPF_ADDR;
4208*0Sstevel@tonic-gate 		} else if (icmp->icmp_sticky_ipp.ipp_fields & IPPF_ADDR) {
4209*0Sstevel@tonic-gate 			option_exists |= IPPF_ADDR;
4210*0Sstevel@tonic-gate 			is_sticky |= IPPF_ADDR;
4211*0Sstevel@tonic-gate 		}
4212*0Sstevel@tonic-gate 	}
4213*0Sstevel@tonic-gate 
4214*0Sstevel@tonic-gate 	if (!(ignore & IPPF_DONTFRAG)) {
4215*0Sstevel@tonic-gate 		if (ipp->ipp_fields & IPPF_DONTFRAG) {
4216*0Sstevel@tonic-gate 			option_exists |= IPPF_DONTFRAG;
4217*0Sstevel@tonic-gate 		} else if (icmp->icmp_sticky_ipp.ipp_fields & IPPF_DONTFRAG) {
4218*0Sstevel@tonic-gate 			option_exists |= IPPF_DONTFRAG;
4219*0Sstevel@tonic-gate 			is_sticky |= IPPF_DONTFRAG;
4220*0Sstevel@tonic-gate 		}
4221*0Sstevel@tonic-gate 	}
4222*0Sstevel@tonic-gate 
4223*0Sstevel@tonic-gate 	if (!(ignore & IPPF_USE_MIN_MTU)) {
4224*0Sstevel@tonic-gate 		if (ipp->ipp_fields & IPPF_USE_MIN_MTU) {
4225*0Sstevel@tonic-gate 			option_exists |= IPPF_USE_MIN_MTU;
4226*0Sstevel@tonic-gate 		} else if (icmp->icmp_sticky_ipp.ipp_fields &
4227*0Sstevel@tonic-gate 		    IPPF_USE_MIN_MTU) {
4228*0Sstevel@tonic-gate 			option_exists |= IPPF_USE_MIN_MTU;
4229*0Sstevel@tonic-gate 			is_sticky |= IPPF_USE_MIN_MTU;
4230*0Sstevel@tonic-gate 		}
4231*0Sstevel@tonic-gate 	}
4232*0Sstevel@tonic-gate 
4233*0Sstevel@tonic-gate 	if (!(ignore & IPPF_NEXTHOP)) {
4234*0Sstevel@tonic-gate 		if (ipp->ipp_fields & IPPF_NEXTHOP) {
4235*0Sstevel@tonic-gate 			option_exists |= IPPF_NEXTHOP;
4236*0Sstevel@tonic-gate 		} else if (icmp->icmp_sticky_ipp.ipp_fields & IPPF_NEXTHOP) {
4237*0Sstevel@tonic-gate 			option_exists |= IPPF_NEXTHOP;
4238*0Sstevel@tonic-gate 			is_sticky |= IPPF_NEXTHOP;
4239*0Sstevel@tonic-gate 		}
4240*0Sstevel@tonic-gate 	}
4241*0Sstevel@tonic-gate 
4242*0Sstevel@tonic-gate 	if (!(ignore & IPPF_HOPLIMIT)) {
4243*0Sstevel@tonic-gate 		if (ipp->ipp_fields & IPPF_HOPLIMIT) {
4244*0Sstevel@tonic-gate 			option_exists |= IPPF_HOPLIMIT;
4245*0Sstevel@tonic-gate 		} else if (icmp->icmp_sticky_ipp.ipp_fields & IPPF_HOPLIMIT) {
4246*0Sstevel@tonic-gate 			option_exists |= IPPF_HOPLIMIT;
4247*0Sstevel@tonic-gate 			is_sticky |= IPPF_HOPLIMIT;
4248*0Sstevel@tonic-gate 		}
4249*0Sstevel@tonic-gate 	}
4250*0Sstevel@tonic-gate 
4251*0Sstevel@tonic-gate 	if (icmp->icmp_sticky_ipp.ipp_fields & IPPF_NO_CKSUM) {
4252*0Sstevel@tonic-gate 		/* This is a sticky socket option only */
4253*0Sstevel@tonic-gate 		option_exists |= IPPF_NO_CKSUM;
4254*0Sstevel@tonic-gate 		is_sticky |= IPPF_NO_CKSUM;
4255*0Sstevel@tonic-gate 	}
4256*0Sstevel@tonic-gate 
4257*0Sstevel@tonic-gate 	if (icmp->icmp_sticky_ipp.ipp_fields & IPPF_RAW_CKSUM) {
4258*0Sstevel@tonic-gate 		/* This is a sticky socket option only */
4259*0Sstevel@tonic-gate 		option_exists |= IPPF_RAW_CKSUM;
4260*0Sstevel@tonic-gate 		is_sticky |= IPPF_RAW_CKSUM;
4261*0Sstevel@tonic-gate 	}
4262*0Sstevel@tonic-gate 
4263*0Sstevel@tonic-gate 	if (!(ignore & IPPF_TCLASS)) {
4264*0Sstevel@tonic-gate 		if (ipp->ipp_fields & IPPF_TCLASS) {
4265*0Sstevel@tonic-gate 			option_exists |= IPPF_TCLASS;
4266*0Sstevel@tonic-gate 		} else if (icmp->icmp_sticky_ipp.ipp_fields & IPPF_TCLASS) {
4267*0Sstevel@tonic-gate 			option_exists |= IPPF_TCLASS;
4268*0Sstevel@tonic-gate 			is_sticky |= IPPF_TCLASS;
4269*0Sstevel@tonic-gate 		}
4270*0Sstevel@tonic-gate 	}
4271*0Sstevel@tonic-gate 
4272*0Sstevel@tonic-gate no_options:
4273*0Sstevel@tonic-gate 
4274*0Sstevel@tonic-gate 	/*
4275*0Sstevel@tonic-gate 	 * If any options carried in the ip6i_t were specified, we
4276*0Sstevel@tonic-gate 	 * need to account for the ip6i_t in the data we'll be sending
4277*0Sstevel@tonic-gate 	 * down.
4278*0Sstevel@tonic-gate 	 */
4279*0Sstevel@tonic-gate 	if (option_exists & IPPF_HAS_IP6I)
4280*0Sstevel@tonic-gate 		ip_hdr_len += sizeof (ip6i_t);
4281*0Sstevel@tonic-gate 
4282*0Sstevel@tonic-gate 	/* check/fix buffer config, setup pointers into it */
4283*0Sstevel@tonic-gate 	mp1 = mp->b_cont;
4284*0Sstevel@tonic-gate 	ip6h = (ip6_t *)&mp1->b_rptr[-ip_hdr_len];
4285*0Sstevel@tonic-gate 	if ((mp1->b_datap->db_ref != 1) ||
4286*0Sstevel@tonic-gate 	    ((unsigned char *)ip6h < mp1->b_datap->db_base) ||
4287*0Sstevel@tonic-gate 	    !OK_32PTR(ip6h)) {
4288*0Sstevel@tonic-gate 		/* Try to get everything in a single mblk next time */
4289*0Sstevel@tonic-gate 		if (ip_hdr_len > icmp->icmp_max_hdr_len) {
4290*0Sstevel@tonic-gate 			icmp->icmp_max_hdr_len = ip_hdr_len;
4291*0Sstevel@tonic-gate 			(void) mi_set_sth_wroff(RD(q),
4292*0Sstevel@tonic-gate 			    icmp->icmp_max_hdr_len + icmp_wroff_extra);
4293*0Sstevel@tonic-gate 		}
4294*0Sstevel@tonic-gate 		mp1 = allocb(ip_hdr_len + icmp_wroff_extra, BPRI_LO);
4295*0Sstevel@tonic-gate 		if (!mp1) {
4296*0Sstevel@tonic-gate 			BUMP_MIB(&rawip_mib, rawipOutErrors);
4297*0Sstevel@tonic-gate 			icmp_ud_err(q, mp, ENOMEM);
4298*0Sstevel@tonic-gate 			return;
4299*0Sstevel@tonic-gate 		}
4300*0Sstevel@tonic-gate 		mp1->b_cont = mp->b_cont;
4301*0Sstevel@tonic-gate 		mp1->b_wptr = mp1->b_datap->db_lim;
4302*0Sstevel@tonic-gate 		ip6h = (ip6_t *)(mp1->b_wptr - ip_hdr_len);
4303*0Sstevel@tonic-gate 	}
4304*0Sstevel@tonic-gate 	mp1->b_rptr = (unsigned char *)ip6h;
4305*0Sstevel@tonic-gate 	ip6i = (ip6i_t *)ip6h;
4306*0Sstevel@tonic-gate 
4307*0Sstevel@tonic-gate #define	ANCIL_OR_STICKY_PTR(f) ((is_sticky & f) ? &icmp->icmp_sticky_ipp : ipp)
4308*0Sstevel@tonic-gate 	if (option_exists & IPPF_HAS_IP6I) {
4309*0Sstevel@tonic-gate 		ip6h = (ip6_t *)&ip6i[1];
4310*0Sstevel@tonic-gate 		ip6i->ip6i_flags = 0;
4311*0Sstevel@tonic-gate 		ip6i->ip6i_vcf = IPV6_DEFAULT_VERS_AND_FLOW;
4312*0Sstevel@tonic-gate 
4313*0Sstevel@tonic-gate 		/* sin6_scope_id takes precendence over IPPF_IFINDEX */
4314*0Sstevel@tonic-gate 		if (option_exists & IPPF_SCOPE_ID) {
4315*0Sstevel@tonic-gate 			ip6i->ip6i_flags |= IP6I_IFINDEX;
4316*0Sstevel@tonic-gate 			ip6i->ip6i_ifindex = sin6->sin6_scope_id;
4317*0Sstevel@tonic-gate 		} else if (option_exists & IPPF_IFINDEX) {
4318*0Sstevel@tonic-gate 			tipp = ANCIL_OR_STICKY_PTR(IPPF_IFINDEX);
4319*0Sstevel@tonic-gate 			ASSERT(tipp->ipp_ifindex != 0);
4320*0Sstevel@tonic-gate 			ip6i->ip6i_flags |= IP6I_IFINDEX;
4321*0Sstevel@tonic-gate 			ip6i->ip6i_ifindex = tipp->ipp_ifindex;
4322*0Sstevel@tonic-gate 		}
4323*0Sstevel@tonic-gate 
4324*0Sstevel@tonic-gate 		if (option_exists & IPPF_RAW_CKSUM) {
4325*0Sstevel@tonic-gate 			ip6i->ip6i_flags |= IP6I_RAW_CHECKSUM;
4326*0Sstevel@tonic-gate 			ip6i->ip6i_checksum_off = icmp->icmp_checksum_off;
4327*0Sstevel@tonic-gate 		}
4328*0Sstevel@tonic-gate 
4329*0Sstevel@tonic-gate 		if (option_exists & IPPF_NO_CKSUM) {
4330*0Sstevel@tonic-gate 			ip6i->ip6i_flags |= IP6I_NO_ULP_CKSUM;
4331*0Sstevel@tonic-gate 		}
4332*0Sstevel@tonic-gate 
4333*0Sstevel@tonic-gate 		if (option_exists & IPPF_ADDR) {
4334*0Sstevel@tonic-gate 			/*
4335*0Sstevel@tonic-gate 			 * Enable per-packet source address verification if
4336*0Sstevel@tonic-gate 			 * IPV6_PKTINFO specified the source address.
4337*0Sstevel@tonic-gate 			 * ip6_src is set in the transport's _wput function.
4338*0Sstevel@tonic-gate 			 */
4339*0Sstevel@tonic-gate 			ip6i->ip6i_flags |= IP6I_VERIFY_SRC;
4340*0Sstevel@tonic-gate 		}
4341*0Sstevel@tonic-gate 
4342*0Sstevel@tonic-gate 		if (option_exists & IPPF_DONTFRAG) {
4343*0Sstevel@tonic-gate 			ip6i->ip6i_flags |= IP6I_DONTFRAG;
4344*0Sstevel@tonic-gate 		}
4345*0Sstevel@tonic-gate 
4346*0Sstevel@tonic-gate 		if (option_exists & IPPF_USE_MIN_MTU) {
4347*0Sstevel@tonic-gate 			ip6i->ip6i_flags = IP6I_API_USE_MIN_MTU(
4348*0Sstevel@tonic-gate 			    ip6i->ip6i_flags, ipp->ipp_use_min_mtu);
4349*0Sstevel@tonic-gate 		}
4350*0Sstevel@tonic-gate 
4351*0Sstevel@tonic-gate 		if (option_exists & IPPF_NEXTHOP) {
4352*0Sstevel@tonic-gate 			tipp = ANCIL_OR_STICKY_PTR(IPPF_NEXTHOP);
4353*0Sstevel@tonic-gate 			ASSERT(!IN6_IS_ADDR_UNSPECIFIED(&tipp->ipp_nexthop));
4354*0Sstevel@tonic-gate 			ip6i->ip6i_flags |= IP6I_NEXTHOP;
4355*0Sstevel@tonic-gate 			ip6i->ip6i_nexthop = tipp->ipp_nexthop;
4356*0Sstevel@tonic-gate 		}
4357*0Sstevel@tonic-gate 
4358*0Sstevel@tonic-gate 		/*
4359*0Sstevel@tonic-gate 		 * tell IP this is an ip6i_t private header
4360*0Sstevel@tonic-gate 		 */
4361*0Sstevel@tonic-gate 		ip6i->ip6i_nxt = IPPROTO_RAW;
4362*0Sstevel@tonic-gate 	}
4363*0Sstevel@tonic-gate 
4364*0Sstevel@tonic-gate 	/* Initialize IPv6 header */
4365*0Sstevel@tonic-gate 	ip6h->ip6_vcf = IPV6_DEFAULT_VERS_AND_FLOW;
4366*0Sstevel@tonic-gate 	bzero(&ip6h->ip6_src, sizeof (ip6h->ip6_src));
4367*0Sstevel@tonic-gate 
4368*0Sstevel@tonic-gate 	if (option_exists & IPPF_HOPLIMIT) {
4369*0Sstevel@tonic-gate 		tipp = ANCIL_OR_STICKY_PTR(IPPF_HOPLIMIT);
4370*0Sstevel@tonic-gate 		ip6h->ip6_hops = tipp->ipp_hoplimit;
4371*0Sstevel@tonic-gate 	} else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr) &&
4372*0Sstevel@tonic-gate 	    (icmp->icmp_sticky_ipp.ipp_fields & IPPF_MULTI_HOPLIMIT)) {
4373*0Sstevel@tonic-gate 		ip6h->ip6_hops = icmp->icmp_multicast_ttl;
4374*0Sstevel@tonic-gate 		ip6i->ip6i_flags |= IP6I_HOPLIMIT;
4375*0Sstevel@tonic-gate 	} else {
4376*0Sstevel@tonic-gate 		ip6h->ip6_hops = icmp->icmp_ttl;
4377*0Sstevel@tonic-gate 	}
4378*0Sstevel@tonic-gate 
4379*0Sstevel@tonic-gate 	if (option_exists & IPPF_ADDR) {
4380*0Sstevel@tonic-gate 		tipp = ANCIL_OR_STICKY_PTR(IPPF_ADDR);
4381*0Sstevel@tonic-gate 		ASSERT(!IN6_IS_ADDR_UNSPECIFIED(&tipp->ipp_addr));
4382*0Sstevel@tonic-gate 		ip6h->ip6_src = tipp->ipp_addr;
4383*0Sstevel@tonic-gate 	} else {
4384*0Sstevel@tonic-gate 		/*
4385*0Sstevel@tonic-gate 		 * The source address was not set using IPV6_PKTINFO.
4386*0Sstevel@tonic-gate 		 * First look at the bound source.
4387*0Sstevel@tonic-gate 		 * If unspecified fallback to __sin6_src_id.
4388*0Sstevel@tonic-gate 		 */
4389*0Sstevel@tonic-gate 		ip6h->ip6_src = icmp->icmp_v6src;
4390*0Sstevel@tonic-gate 		if (sin6->__sin6_src_id != 0 &&
4391*0Sstevel@tonic-gate 		    IN6_IS_ADDR_UNSPECIFIED(&ip6h->ip6_src)) {
4392*0Sstevel@tonic-gate 			ip_srcid_find_id(sin6->__sin6_src_id,
4393*0Sstevel@tonic-gate 			    &ip6h->ip6_src, icmp->icmp_zoneid);
4394*0Sstevel@tonic-gate 		}
4395*0Sstevel@tonic-gate 	}
4396*0Sstevel@tonic-gate 
4397*0Sstevel@tonic-gate 	nxthdr_ptr = (uint8_t *)&ip6h->ip6_nxt;
4398*0Sstevel@tonic-gate 	cp = (uint8_t *)&ip6h[1];
4399*0Sstevel@tonic-gate 
4400*0Sstevel@tonic-gate 	/*
4401*0Sstevel@tonic-gate 	 * Here's where we have to start stringing together
4402*0Sstevel@tonic-gate 	 * any extension headers in the right order:
4403*0Sstevel@tonic-gate 	 * Hop-by-hop, destination, routing, and final destination opts.
4404*0Sstevel@tonic-gate 	 */
4405*0Sstevel@tonic-gate 	if (option_exists & IPPF_HOPOPTS) {
4406*0Sstevel@tonic-gate 		/* Hop-by-hop options */
4407*0Sstevel@tonic-gate 		ip6_hbh_t *hbh = (ip6_hbh_t *)cp;
4408*0Sstevel@tonic-gate 		tipp = ANCIL_OR_STICKY_PTR(IPPF_HOPOPTS);
4409*0Sstevel@tonic-gate 
4410*0Sstevel@tonic-gate 		*nxthdr_ptr = IPPROTO_HOPOPTS;
4411*0Sstevel@tonic-gate 		nxthdr_ptr = &hbh->ip6h_nxt;
4412*0Sstevel@tonic-gate 
4413*0Sstevel@tonic-gate 		bcopy(tipp->ipp_hopopts, cp, tipp->ipp_hopoptslen);
4414*0Sstevel@tonic-gate 		cp += tipp->ipp_hopoptslen;
4415*0Sstevel@tonic-gate 	}
4416*0Sstevel@tonic-gate 	/*
4417*0Sstevel@tonic-gate 	 * En-route destination options
4418*0Sstevel@tonic-gate 	 * Only do them if there's a routing header as well
4419*0Sstevel@tonic-gate 	 */
4420*0Sstevel@tonic-gate 	if (option_exists & IPPF_RTDSTOPTS) {
4421*0Sstevel@tonic-gate 		ip6_dest_t *dst = (ip6_dest_t *)cp;
4422*0Sstevel@tonic-gate 		tipp = ANCIL_OR_STICKY_PTR(IPPF_RTDSTOPTS);
4423*0Sstevel@tonic-gate 
4424*0Sstevel@tonic-gate 		*nxthdr_ptr = IPPROTO_DSTOPTS;
4425*0Sstevel@tonic-gate 		nxthdr_ptr = &dst->ip6d_nxt;
4426*0Sstevel@tonic-gate 
4427*0Sstevel@tonic-gate 		bcopy(tipp->ipp_rtdstopts, cp, tipp->ipp_rtdstoptslen);
4428*0Sstevel@tonic-gate 		cp += tipp->ipp_rtdstoptslen;
4429*0Sstevel@tonic-gate 	}
4430*0Sstevel@tonic-gate 	/*
4431*0Sstevel@tonic-gate 	 * Routing header next
4432*0Sstevel@tonic-gate 	 */
4433*0Sstevel@tonic-gate 	if (option_exists & IPPF_RTHDR) {
4434*0Sstevel@tonic-gate 		ip6_rthdr_t *rt = (ip6_rthdr_t *)cp;
4435*0Sstevel@tonic-gate 		tipp = ANCIL_OR_STICKY_PTR(IPPF_RTHDR);
4436*0Sstevel@tonic-gate 
4437*0Sstevel@tonic-gate 		*nxthdr_ptr = IPPROTO_ROUTING;
4438*0Sstevel@tonic-gate 		nxthdr_ptr = &rt->ip6r_nxt;
4439*0Sstevel@tonic-gate 
4440*0Sstevel@tonic-gate 		bcopy(tipp->ipp_rthdr, cp, tipp->ipp_rthdrlen);
4441*0Sstevel@tonic-gate 		cp += tipp->ipp_rthdrlen;
4442*0Sstevel@tonic-gate 	}
4443*0Sstevel@tonic-gate 	/*
4444*0Sstevel@tonic-gate 	 * Do ultimate destination options
4445*0Sstevel@tonic-gate 	 */
4446*0Sstevel@tonic-gate 	if (option_exists & IPPF_DSTOPTS) {
4447*0Sstevel@tonic-gate 		ip6_dest_t *dest = (ip6_dest_t *)cp;
4448*0Sstevel@tonic-gate 		tipp = ANCIL_OR_STICKY_PTR(IPPF_DSTOPTS);
4449*0Sstevel@tonic-gate 
4450*0Sstevel@tonic-gate 		*nxthdr_ptr = IPPROTO_DSTOPTS;
4451*0Sstevel@tonic-gate 		nxthdr_ptr = &dest->ip6d_nxt;
4452*0Sstevel@tonic-gate 
4453*0Sstevel@tonic-gate 		bcopy(tipp->ipp_dstopts, cp, tipp->ipp_dstoptslen);
4454*0Sstevel@tonic-gate 		cp += tipp->ipp_dstoptslen;
4455*0Sstevel@tonic-gate 	}
4456*0Sstevel@tonic-gate 
4457*0Sstevel@tonic-gate 	/*
4458*0Sstevel@tonic-gate 	 * Now set the last header pointer to the proto passed in
4459*0Sstevel@tonic-gate 	 */
4460*0Sstevel@tonic-gate 	ASSERT((int)(cp - (uint8_t *)ip6i) == ip_hdr_len);
4461*0Sstevel@tonic-gate 	*nxthdr_ptr = icmp->icmp_proto;
4462*0Sstevel@tonic-gate 
4463*0Sstevel@tonic-gate 	/*
4464*0Sstevel@tonic-gate 	 * Copy in the destination address
4465*0Sstevel@tonic-gate 	 */
4466*0Sstevel@tonic-gate 	if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr))
4467*0Sstevel@tonic-gate 		ip6h->ip6_dst = ipv6_loopback;
4468*0Sstevel@tonic-gate 	else
4469*0Sstevel@tonic-gate 		ip6h->ip6_dst = sin6->sin6_addr;
4470*0Sstevel@tonic-gate 
4471*0Sstevel@tonic-gate 	ip6h->ip6_vcf =
4472*0Sstevel@tonic-gate 		(IPV6_DEFAULT_VERS_AND_FLOW & IPV6_VERS_AND_FLOW_MASK) |
4473*0Sstevel@tonic-gate 		(sin6->sin6_flowinfo & ~IPV6_VERS_AND_FLOW_MASK);
4474*0Sstevel@tonic-gate 
4475*0Sstevel@tonic-gate 	if (option_exists & IPPF_TCLASS) {
4476*0Sstevel@tonic-gate 		tipp = ANCIL_OR_STICKY_PTR(IPPF_TCLASS);
4477*0Sstevel@tonic-gate 		ip6h->ip6_vcf = IPV6_TCLASS_FLOW(ip6h->ip6_vcf,
4478*0Sstevel@tonic-gate 		    tipp->ipp_tclass);
4479*0Sstevel@tonic-gate 	}
4480*0Sstevel@tonic-gate 	if (option_exists & IPPF_RTHDR) {
4481*0Sstevel@tonic-gate 		ip6_rthdr_t	*rth;
4482*0Sstevel@tonic-gate 
4483*0Sstevel@tonic-gate 		/*
4484*0Sstevel@tonic-gate 		 * Perform any processing needed for source routing.
4485*0Sstevel@tonic-gate 		 * We know that all extension headers will be in the same mblk
4486*0Sstevel@tonic-gate 		 * as the IPv6 header.
4487*0Sstevel@tonic-gate 		 */
4488*0Sstevel@tonic-gate 		rth = ip_find_rthdr_v6(ip6h, mp1->b_wptr);
4489*0Sstevel@tonic-gate 		if (rth != NULL && rth->ip6r_segleft != 0) {
4490*0Sstevel@tonic-gate 			if (rth->ip6r_type != IPV6_RTHDR_TYPE_0) {
4491*0Sstevel@tonic-gate 				/*
4492*0Sstevel@tonic-gate 				 * Drop packet - only support Type 0 routing.
4493*0Sstevel@tonic-gate 				 * Notify the application as well.
4494*0Sstevel@tonic-gate 				 */
4495*0Sstevel@tonic-gate 				icmp_ud_err(q, mp, EPROTO);
4496*0Sstevel@tonic-gate 				BUMP_MIB(&rawip_mib, rawipOutErrors);
4497*0Sstevel@tonic-gate 				return;
4498*0Sstevel@tonic-gate 			}
4499*0Sstevel@tonic-gate 			/*
4500*0Sstevel@tonic-gate 			 * rth->ip6r_len is twice the number of
4501*0Sstevel@tonic-gate 			 * addresses in the header
4502*0Sstevel@tonic-gate 			 */
4503*0Sstevel@tonic-gate 			if (rth->ip6r_len & 0x1) {
4504*0Sstevel@tonic-gate 				icmp_ud_err(q, mp, EPROTO);
4505*0Sstevel@tonic-gate 				BUMP_MIB(&rawip_mib, rawipOutErrors);
4506*0Sstevel@tonic-gate 				return;
4507*0Sstevel@tonic-gate 			}
4508*0Sstevel@tonic-gate 			/*
4509*0Sstevel@tonic-gate 			 * Shuffle the routing header and ip6_dst
4510*0Sstevel@tonic-gate 			 * addresses, and get the checksum difference
4511*0Sstevel@tonic-gate 			 * between the first hop (in ip6_dst) and
4512*0Sstevel@tonic-gate 			 * the destination (in the last routing hdr entry).
4513*0Sstevel@tonic-gate 			 */
4514*0Sstevel@tonic-gate 			csum = ip_massage_options_v6(ip6h, rth);
4515*0Sstevel@tonic-gate 			/*
4516*0Sstevel@tonic-gate 			 * Verify that the first hop isn't a mapped address.
4517*0Sstevel@tonic-gate 			 * Routers along the path need to do this verification
4518*0Sstevel@tonic-gate 			 * for subsequent hops.
4519*0Sstevel@tonic-gate 			 */
4520*0Sstevel@tonic-gate 			if (IN6_IS_ADDR_V4MAPPED(&ip6h->ip6_dst)) {
4521*0Sstevel@tonic-gate 				icmp_ud_err(q, mp, EADDRNOTAVAIL);
4522*0Sstevel@tonic-gate 				BUMP_MIB(&rawip_mib, rawipOutErrors);
4523*0Sstevel@tonic-gate 				return;
4524*0Sstevel@tonic-gate 			}
4525*0Sstevel@tonic-gate 		}
4526*0Sstevel@tonic-gate 	}
4527*0Sstevel@tonic-gate 
4528*0Sstevel@tonic-gate 	ip_len = mp1->b_wptr - (uchar_t *)ip6h - IPV6_HDR_LEN;
4529*0Sstevel@tonic-gate 	if (mp1->b_cont != NULL)
4530*0Sstevel@tonic-gate 		ip_len += msgdsize(mp1->b_cont);
4531*0Sstevel@tonic-gate 
4532*0Sstevel@tonic-gate 	/*
4533*0Sstevel@tonic-gate 	 * Set the length into the IP header.
4534*0Sstevel@tonic-gate 	 * If the length is greater than the maximum allowed by IP,
4535*0Sstevel@tonic-gate 	 * then free the message and return. Do not try and send it
4536*0Sstevel@tonic-gate 	 * as this can cause problems in layers below.
4537*0Sstevel@tonic-gate 	 */
4538*0Sstevel@tonic-gate 	if (ip_len > IP_MAXPACKET) {
4539*0Sstevel@tonic-gate 		BUMP_MIB(&rawip_mib, rawipOutErrors);
4540*0Sstevel@tonic-gate 		icmp_ud_err(q, mp1, EMSGSIZE);
4541*0Sstevel@tonic-gate 		return;
4542*0Sstevel@tonic-gate 	}
4543*0Sstevel@tonic-gate 	if (icmp->icmp_proto == IPPROTO_ICMPV6 || icmp->icmp_raw_checksum) {
4544*0Sstevel@tonic-gate 		uint_t	cksum_off;	/* From ip6i == mp1->b_rptr */
4545*0Sstevel@tonic-gate 		uint16_t *cksum_ptr;
4546*0Sstevel@tonic-gate 		uint_t	ext_hdrs_len;
4547*0Sstevel@tonic-gate 
4548*0Sstevel@tonic-gate 		/* ICMPv6 must have an offset matching icmp6_cksum offset */
4549*0Sstevel@tonic-gate 		ASSERT(icmp->icmp_proto != IPPROTO_ICMPV6 ||
4550*0Sstevel@tonic-gate 		    icmp->icmp_checksum_off == 2);
4551*0Sstevel@tonic-gate 
4552*0Sstevel@tonic-gate 		/*
4553*0Sstevel@tonic-gate 		 * We make it easy for IP to include our pseudo header
4554*0Sstevel@tonic-gate 		 * by putting our length in uh_checksum, modified (if
4555*0Sstevel@tonic-gate 		 * we have a routing header) by the checksum difference
4556*0Sstevel@tonic-gate 		 * between the ultimate destination and first hop addresses.
4557*0Sstevel@tonic-gate 		 * Note: ICMPv6 must always checksum the packet.
4558*0Sstevel@tonic-gate 		 */
4559*0Sstevel@tonic-gate 		cksum_off = ip_hdr_len + icmp->icmp_checksum_off;
4560*0Sstevel@tonic-gate 		if (cksum_off + sizeof (uint16_t) > mp1->b_wptr - mp1->b_rptr) {
4561*0Sstevel@tonic-gate 			if (!pullupmsg(mp1, cksum_off + sizeof (uint16_t))) {
4562*0Sstevel@tonic-gate 				BUMP_MIB(&rawip_mib, rawipOutErrors);
4563*0Sstevel@tonic-gate 				freemsg(mp);
4564*0Sstevel@tonic-gate 				return;
4565*0Sstevel@tonic-gate 			}
4566*0Sstevel@tonic-gate 			ip6i = (ip6i_t *)mp1->b_rptr;
4567*0Sstevel@tonic-gate 			if (ip6i->ip6i_nxt == IPPROTO_RAW)
4568*0Sstevel@tonic-gate 				ip6h = (ip6_t *)&ip6i[1];
4569*0Sstevel@tonic-gate 			else
4570*0Sstevel@tonic-gate 				ip6h = (ip6_t *)ip6i;
4571*0Sstevel@tonic-gate 		}
4572*0Sstevel@tonic-gate 		/* Add payload length to checksum */
4573*0Sstevel@tonic-gate 		ext_hdrs_len = ip_hdr_len - IPV6_HDR_LEN -
4574*0Sstevel@tonic-gate 		    (int)((uchar_t *)ip6h - (uchar_t *)ip6i);
4575*0Sstevel@tonic-gate 		csum += htons(ip_len - ext_hdrs_len);
4576*0Sstevel@tonic-gate 
4577*0Sstevel@tonic-gate 		cksum_ptr = (uint16_t *)((uchar_t *)ip6i + cksum_off);
4578*0Sstevel@tonic-gate 		csum = (csum & 0xFFFF) + (csum >> 16);
4579*0Sstevel@tonic-gate 		*cksum_ptr = (uint16_t)csum;
4580*0Sstevel@tonic-gate 	}
4581*0Sstevel@tonic-gate 
4582*0Sstevel@tonic-gate #ifdef _LITTLE_ENDIAN
4583*0Sstevel@tonic-gate 	ip_len = htons(ip_len);
4584*0Sstevel@tonic-gate #endif
4585*0Sstevel@tonic-gate 	ip6h->ip6_plen = (uint16_t)ip_len;
4586*0Sstevel@tonic-gate 
4587*0Sstevel@tonic-gate 	freeb(mp);
4588*0Sstevel@tonic-gate 
4589*0Sstevel@tonic-gate 	/* We're done. Pass the packet to IP */
4590*0Sstevel@tonic-gate 	BUMP_MIB(&rawip_mib, rawipOutDatagrams);
4591*0Sstevel@tonic-gate 	putnext(q, mp1);
4592*0Sstevel@tonic-gate }
4593*0Sstevel@tonic-gate 
4594*0Sstevel@tonic-gate static void
4595*0Sstevel@tonic-gate icmp_wput_other(queue_t *q, mblk_t *mp)
4596*0Sstevel@tonic-gate {
4597*0Sstevel@tonic-gate 	uchar_t	*rptr = mp->b_rptr;
4598*0Sstevel@tonic-gate 	struct iocblk *iocp;
4599*0Sstevel@tonic-gate #define	tudr ((struct T_unitdata_req *)rptr)
4600*0Sstevel@tonic-gate 	icmp_t	*icmp;
4601*0Sstevel@tonic-gate 	cred_t *cr;
4602*0Sstevel@tonic-gate 
4603*0Sstevel@tonic-gate 	icmp = (icmp_t *)q->q_ptr;
4604*0Sstevel@tonic-gate 
4605*0Sstevel@tonic-gate 	cr = DB_CREDDEF(mp, icmp->icmp_credp);
4606*0Sstevel@tonic-gate 
4607*0Sstevel@tonic-gate 	switch (mp->b_datap->db_type) {
4608*0Sstevel@tonic-gate 	case M_PROTO:
4609*0Sstevel@tonic-gate 	case M_PCPROTO:
4610*0Sstevel@tonic-gate 		if (mp->b_wptr - rptr < sizeof (t_scalar_t)) {
4611*0Sstevel@tonic-gate 			/*
4612*0Sstevel@tonic-gate 			 * If the message does not contain a PRIM_type,
4613*0Sstevel@tonic-gate 			 * throw it away.
4614*0Sstevel@tonic-gate 			 */
4615*0Sstevel@tonic-gate 			freemsg(mp);
4616*0Sstevel@tonic-gate 			return;
4617*0Sstevel@tonic-gate 		}
4618*0Sstevel@tonic-gate 		switch (((union T_primitives *)rptr)->type) {
4619*0Sstevel@tonic-gate 		case T_ADDR_REQ:
4620*0Sstevel@tonic-gate 			icmp_addr_req(q, mp);
4621*0Sstevel@tonic-gate 			return;
4622*0Sstevel@tonic-gate 		case O_T_BIND_REQ:
4623*0Sstevel@tonic-gate 		case T_BIND_REQ:
4624*0Sstevel@tonic-gate 			qwriter(q, mp, icmp_bind, PERIM_OUTER);
4625*0Sstevel@tonic-gate 			return;
4626*0Sstevel@tonic-gate 		case T_CONN_REQ:
4627*0Sstevel@tonic-gate 			icmp_connect(q, mp);
4628*0Sstevel@tonic-gate 			return;
4629*0Sstevel@tonic-gate 		case T_CAPABILITY_REQ:
4630*0Sstevel@tonic-gate 			icmp_capability_req(q, mp);
4631*0Sstevel@tonic-gate 			return;
4632*0Sstevel@tonic-gate 		case T_INFO_REQ:
4633*0Sstevel@tonic-gate 			icmp_info_req(q, mp);
4634*0Sstevel@tonic-gate 			return;
4635*0Sstevel@tonic-gate 		case T_UNITDATA_REQ:
4636*0Sstevel@tonic-gate 			/*
4637*0Sstevel@tonic-gate 			 * If a T_UNITDATA_REQ gets here, the address must
4638*0Sstevel@tonic-gate 			 * be bad.  Valid T_UNITDATA_REQs are found above
4639*0Sstevel@tonic-gate 			 * and break to below this switch.
4640*0Sstevel@tonic-gate 			 */
4641*0Sstevel@tonic-gate 			icmp_ud_err(q, mp, EADDRNOTAVAIL);
4642*0Sstevel@tonic-gate 			return;
4643*0Sstevel@tonic-gate 		case T_UNBIND_REQ:
4644*0Sstevel@tonic-gate 			icmp_unbind(q, mp);
4645*0Sstevel@tonic-gate 			return;
4646*0Sstevel@tonic-gate 
4647*0Sstevel@tonic-gate 		case T_SVR4_OPTMGMT_REQ:
4648*0Sstevel@tonic-gate 			if (!snmpcom_req(q, mp, icmp_snmp_set, icmp_snmp_get,
4649*0Sstevel@tonic-gate 			    cr))
4650*0Sstevel@tonic-gate 				/* Only IP can return anything meaningful */
4651*0Sstevel@tonic-gate 				(void) svr4_optcom_req(q, mp, cr,
4652*0Sstevel@tonic-gate 				    &icmp_opt_obj);
4653*0Sstevel@tonic-gate 			return;
4654*0Sstevel@tonic-gate 
4655*0Sstevel@tonic-gate 		case T_OPTMGMT_REQ:
4656*0Sstevel@tonic-gate 			/* Only IP can return anything meaningful */
4657*0Sstevel@tonic-gate 			(void) tpi_optcom_req(q, mp, cr, &icmp_opt_obj);
4658*0Sstevel@tonic-gate 			return;
4659*0Sstevel@tonic-gate 
4660*0Sstevel@tonic-gate 		case T_DISCON_REQ:
4661*0Sstevel@tonic-gate 			icmp_disconnect(q, mp);
4662*0Sstevel@tonic-gate 			return;
4663*0Sstevel@tonic-gate 
4664*0Sstevel@tonic-gate 		/* The following TPI message is not supported by icmp. */
4665*0Sstevel@tonic-gate 		case O_T_CONN_RES:
4666*0Sstevel@tonic-gate 		case T_CONN_RES:
4667*0Sstevel@tonic-gate 			icmp_err_ack(q, mp, TNOTSUPPORT, 0);
4668*0Sstevel@tonic-gate 			return;
4669*0Sstevel@tonic-gate 
4670*0Sstevel@tonic-gate 		/* The following 3 TPI requests are illegal for icmp. */
4671*0Sstevel@tonic-gate 		case T_DATA_REQ:
4672*0Sstevel@tonic-gate 		case T_EXDATA_REQ:
4673*0Sstevel@tonic-gate 		case T_ORDREL_REQ:
4674*0Sstevel@tonic-gate 			freemsg(mp);
4675*0Sstevel@tonic-gate 			(void) putctl1(RD(q), M_ERROR, EPROTO);
4676*0Sstevel@tonic-gate 			return;
4677*0Sstevel@tonic-gate 		default:
4678*0Sstevel@tonic-gate 			break;
4679*0Sstevel@tonic-gate 		}
4680*0Sstevel@tonic-gate 		break;
4681*0Sstevel@tonic-gate 	case M_IOCTL:
4682*0Sstevel@tonic-gate 		iocp = (struct iocblk *)mp->b_rptr;
4683*0Sstevel@tonic-gate 		switch (iocp->ioc_cmd) {
4684*0Sstevel@tonic-gate 		case TI_GETPEERNAME:
4685*0Sstevel@tonic-gate 			if (icmp->icmp_state != TS_DATA_XFER) {
4686*0Sstevel@tonic-gate 				/*
4687*0Sstevel@tonic-gate 				 * If a default destination address has not
4688*0Sstevel@tonic-gate 				 * been associated with the stream, then we
4689*0Sstevel@tonic-gate 				 * don't know the peer's name.
4690*0Sstevel@tonic-gate 				 */
4691*0Sstevel@tonic-gate 				iocp->ioc_error = ENOTCONN;
4692*0Sstevel@tonic-gate 			    err_ret:;
4693*0Sstevel@tonic-gate 				iocp->ioc_count = 0;
4694*0Sstevel@tonic-gate 				mp->b_datap->db_type = M_IOCACK;
4695*0Sstevel@tonic-gate 				qreply(q, mp);
4696*0Sstevel@tonic-gate 				return;
4697*0Sstevel@tonic-gate 			}
4698*0Sstevel@tonic-gate 			/* FALLTHRU */
4699*0Sstevel@tonic-gate 		case TI_GETMYNAME:
4700*0Sstevel@tonic-gate 			/*
4701*0Sstevel@tonic-gate 			 * For TI_GETPEERNAME and TI_GETMYNAME, we first
4702*0Sstevel@tonic-gate 			 * need to copyin the user's strbuf structure.
4703*0Sstevel@tonic-gate 			 * Processing will continue in the M_IOCDATA case
4704*0Sstevel@tonic-gate 			 * below.
4705*0Sstevel@tonic-gate 			 */
4706*0Sstevel@tonic-gate 			mi_copyin(q, mp, NULL,
4707*0Sstevel@tonic-gate 			    SIZEOF_STRUCT(strbuf, iocp->ioc_flag));
4708*0Sstevel@tonic-gate 			return;
4709*0Sstevel@tonic-gate 		case ND_SET:
4710*0Sstevel@tonic-gate 			/* nd_getset performs the necessary error checking */
4711*0Sstevel@tonic-gate 		case ND_GET:
4712*0Sstevel@tonic-gate 			if (nd_getset(q, icmp_g_nd, mp)) {
4713*0Sstevel@tonic-gate 				qreply(q, mp);
4714*0Sstevel@tonic-gate 				return;
4715*0Sstevel@tonic-gate 			}
4716*0Sstevel@tonic-gate 			break;
4717*0Sstevel@tonic-gate 		default:
4718*0Sstevel@tonic-gate 			break;
4719*0Sstevel@tonic-gate 		}
4720*0Sstevel@tonic-gate 		break;
4721*0Sstevel@tonic-gate 	case M_IOCDATA:
4722*0Sstevel@tonic-gate 		icmp_wput_iocdata(q, mp);
4723*0Sstevel@tonic-gate 		return;
4724*0Sstevel@tonic-gate 	default:
4725*0Sstevel@tonic-gate 		break;
4726*0Sstevel@tonic-gate 	}
4727*0Sstevel@tonic-gate 	putnext(q, mp);
4728*0Sstevel@tonic-gate }
4729*0Sstevel@tonic-gate 
4730*0Sstevel@tonic-gate /*
4731*0Sstevel@tonic-gate  * icmp_wput_iocdata is called by icmp_wput_slow to handle all M_IOCDATA
4732*0Sstevel@tonic-gate  * messages.
4733*0Sstevel@tonic-gate  */
4734*0Sstevel@tonic-gate static void
4735*0Sstevel@tonic-gate icmp_wput_iocdata(queue_t *q, mblk_t *mp)
4736*0Sstevel@tonic-gate {
4737*0Sstevel@tonic-gate 	mblk_t	*mp1;
4738*0Sstevel@tonic-gate 	STRUCT_HANDLE(strbuf, sb);
4739*0Sstevel@tonic-gate 	icmp_t	*icmp;
4740*0Sstevel@tonic-gate 	in6_addr_t	v6addr;
4741*0Sstevel@tonic-gate 	ipaddr_t	v4addr;
4742*0Sstevel@tonic-gate 	uint32_t	flowinfo = 0;
4743*0Sstevel@tonic-gate 	int		addrlen;
4744*0Sstevel@tonic-gate 
4745*0Sstevel@tonic-gate 	/* Make sure it is one of ours. */
4746*0Sstevel@tonic-gate 	switch (((struct iocblk *)mp->b_rptr)->ioc_cmd) {
4747*0Sstevel@tonic-gate 	case TI_GETMYNAME:
4748*0Sstevel@tonic-gate 	case TI_GETPEERNAME:
4749*0Sstevel@tonic-gate 		break;
4750*0Sstevel@tonic-gate 	default:
4751*0Sstevel@tonic-gate 		putnext(q, mp);
4752*0Sstevel@tonic-gate 		return;
4753*0Sstevel@tonic-gate 	}
4754*0Sstevel@tonic-gate 	switch (mi_copy_state(q, mp, &mp1)) {
4755*0Sstevel@tonic-gate 	case -1:
4756*0Sstevel@tonic-gate 		return;
4757*0Sstevel@tonic-gate 	case MI_COPY_CASE(MI_COPY_IN, 1):
4758*0Sstevel@tonic-gate 		break;
4759*0Sstevel@tonic-gate 	case MI_COPY_CASE(MI_COPY_OUT, 1):
4760*0Sstevel@tonic-gate 		/*
4761*0Sstevel@tonic-gate 		 * The address has been copied out, so now
4762*0Sstevel@tonic-gate 		 * copyout the strbuf.
4763*0Sstevel@tonic-gate 		 */
4764*0Sstevel@tonic-gate 		mi_copyout(q, mp);
4765*0Sstevel@tonic-gate 		return;
4766*0Sstevel@tonic-gate 	case MI_COPY_CASE(MI_COPY_OUT, 2):
4767*0Sstevel@tonic-gate 		/*
4768*0Sstevel@tonic-gate 		 * The address and strbuf have been copied out.
4769*0Sstevel@tonic-gate 		 * We're done, so just acknowledge the original
4770*0Sstevel@tonic-gate 		 * M_IOCTL.
4771*0Sstevel@tonic-gate 		 */
4772*0Sstevel@tonic-gate 		mi_copy_done(q, mp, 0);
4773*0Sstevel@tonic-gate 		return;
4774*0Sstevel@tonic-gate 	default:
4775*0Sstevel@tonic-gate 		/*
4776*0Sstevel@tonic-gate 		 * Something strange has happened, so acknowledge
4777*0Sstevel@tonic-gate 		 * the original M_IOCTL with an EPROTO error.
4778*0Sstevel@tonic-gate 		 */
4779*0Sstevel@tonic-gate 		mi_copy_done(q, mp, EPROTO);
4780*0Sstevel@tonic-gate 		return;
4781*0Sstevel@tonic-gate 	}
4782*0Sstevel@tonic-gate 	/*
4783*0Sstevel@tonic-gate 	 * Now we have the strbuf structure for TI_GETMYNAME
4784*0Sstevel@tonic-gate 	 * and TI_GETPEERNAME.  Next we copyout the requested
4785*0Sstevel@tonic-gate 	 * address and then we'll copyout the strbuf.
4786*0Sstevel@tonic-gate 	 */
4787*0Sstevel@tonic-gate 	STRUCT_SET_HANDLE(sb, ((struct iocblk *)mp->b_rptr)->ioc_flag,
4788*0Sstevel@tonic-gate 	    (void *)mp1->b_rptr);
4789*0Sstevel@tonic-gate 	icmp = (icmp_t *)q->q_ptr;
4790*0Sstevel@tonic-gate 	if (icmp->icmp_family == AF_INET)
4791*0Sstevel@tonic-gate 		addrlen = sizeof (sin_t);
4792*0Sstevel@tonic-gate 	else
4793*0Sstevel@tonic-gate 		addrlen = sizeof (sin6_t);
4794*0Sstevel@tonic-gate 
4795*0Sstevel@tonic-gate 	if (STRUCT_FGET(sb, maxlen) < addrlen) {
4796*0Sstevel@tonic-gate 		mi_copy_done(q, mp, EINVAL);
4797*0Sstevel@tonic-gate 		return;
4798*0Sstevel@tonic-gate 	}
4799*0Sstevel@tonic-gate 	switch (((struct iocblk *)mp->b_rptr)->ioc_cmd) {
4800*0Sstevel@tonic-gate 	case TI_GETMYNAME:
4801*0Sstevel@tonic-gate 		if (icmp->icmp_family == AF_INET) {
4802*0Sstevel@tonic-gate 			ASSERT(icmp->icmp_ipversion == IPV4_VERSION);
4803*0Sstevel@tonic-gate 			if (!IN6_IS_ADDR_V4MAPPED_ANY(&icmp->icmp_v6src) &&
4804*0Sstevel@tonic-gate 			    !IN6_IS_ADDR_UNSPECIFIED(&icmp->icmp_v6src)) {
4805*0Sstevel@tonic-gate 				v4addr = V4_PART_OF_V6(icmp->icmp_v6src);
4806*0Sstevel@tonic-gate 			} else {
4807*0Sstevel@tonic-gate 				/*
4808*0Sstevel@tonic-gate 				 * INADDR_ANY
4809*0Sstevel@tonic-gate 				 * icmp_v6src is not set, we might be bound to
4810*0Sstevel@tonic-gate 				 * broadcast/multicast. Use icmp_bound_v6src as
4811*0Sstevel@tonic-gate 				 * local address instead (that could
4812*0Sstevel@tonic-gate 				 * also still be INADDR_ANY)
4813*0Sstevel@tonic-gate 				 */
4814*0Sstevel@tonic-gate 				v4addr = V4_PART_OF_V6(icmp->icmp_bound_v6src);
4815*0Sstevel@tonic-gate 			}
4816*0Sstevel@tonic-gate 		} else {
4817*0Sstevel@tonic-gate 			/* icmp->icmp_family == AF_INET6 */
4818*0Sstevel@tonic-gate 			if (!IN6_IS_ADDR_UNSPECIFIED(&icmp->icmp_v6src)) {
4819*0Sstevel@tonic-gate 				v6addr = icmp->icmp_v6src;
4820*0Sstevel@tonic-gate 			} else {
4821*0Sstevel@tonic-gate 				/*
4822*0Sstevel@tonic-gate 				 * UNSPECIFIED
4823*0Sstevel@tonic-gate 				 * icmp_v6src is not set, we might be bound to
4824*0Sstevel@tonic-gate 				 * broadcast/multicast. Use icmp_bound_v6src as
4825*0Sstevel@tonic-gate 				 * local address instead (that could
4826*0Sstevel@tonic-gate 				 * also still be UNSPECIFIED)
4827*0Sstevel@tonic-gate 				 */
4828*0Sstevel@tonic-gate 				v6addr = icmp->icmp_bound_v6src;
4829*0Sstevel@tonic-gate 			}
4830*0Sstevel@tonic-gate 		}
4831*0Sstevel@tonic-gate 		break;
4832*0Sstevel@tonic-gate 	case TI_GETPEERNAME:
4833*0Sstevel@tonic-gate 		if (icmp->icmp_family == AF_INET) {
4834*0Sstevel@tonic-gate 			ASSERT(icmp->icmp_ipversion == IPV4_VERSION);
4835*0Sstevel@tonic-gate 			v4addr = V4_PART_OF_V6(icmp->icmp_v6dst);
4836*0Sstevel@tonic-gate 		} else {
4837*0Sstevel@tonic-gate 			/* icmp->icmp_family == AF_INET6) */
4838*0Sstevel@tonic-gate 			v6addr = icmp->icmp_v6dst;
4839*0Sstevel@tonic-gate 			flowinfo = icmp->icmp_flowinfo;
4840*0Sstevel@tonic-gate 		}
4841*0Sstevel@tonic-gate 		break;
4842*0Sstevel@tonic-gate 	default:
4843*0Sstevel@tonic-gate 		mi_copy_done(q, mp, EPROTO);
4844*0Sstevel@tonic-gate 		return;
4845*0Sstevel@tonic-gate 	}
4846*0Sstevel@tonic-gate 	mp1 = mi_copyout_alloc(q, mp, STRUCT_FGETP(sb, buf), addrlen, B_TRUE);
4847*0Sstevel@tonic-gate 	if (!mp1)
4848*0Sstevel@tonic-gate 		return;
4849*0Sstevel@tonic-gate 
4850*0Sstevel@tonic-gate 	if (icmp->icmp_family == AF_INET) {
4851*0Sstevel@tonic-gate 		sin_t *sin;
4852*0Sstevel@tonic-gate 
4853*0Sstevel@tonic-gate 		STRUCT_FSET(sb, len, (int)sizeof (sin_t));
4854*0Sstevel@tonic-gate 		sin = (sin_t *)mp1->b_rptr;
4855*0Sstevel@tonic-gate 		mp1->b_wptr = (uchar_t *)&sin[1];
4856*0Sstevel@tonic-gate 		*sin = sin_null;
4857*0Sstevel@tonic-gate 		sin->sin_family = AF_INET;
4858*0Sstevel@tonic-gate 		sin->sin_addr.s_addr = v4addr;
4859*0Sstevel@tonic-gate 	} else {
4860*0Sstevel@tonic-gate 		/* icmp->icmp_family == AF_INET6 */
4861*0Sstevel@tonic-gate 		sin6_t *sin6;
4862*0Sstevel@tonic-gate 
4863*0Sstevel@tonic-gate 		ASSERT(icmp->icmp_family == AF_INET6);
4864*0Sstevel@tonic-gate 		STRUCT_FSET(sb, len, (int)sizeof (sin6_t));
4865*0Sstevel@tonic-gate 		sin6 = (sin6_t *)mp1->b_rptr;
4866*0Sstevel@tonic-gate 		mp1->b_wptr = (uchar_t *)&sin6[1];
4867*0Sstevel@tonic-gate 		*sin6 = sin6_null;
4868*0Sstevel@tonic-gate 		sin6->sin6_family = AF_INET6;
4869*0Sstevel@tonic-gate 		sin6->sin6_flowinfo = flowinfo;
4870*0Sstevel@tonic-gate 		sin6->sin6_addr = v6addr;
4871*0Sstevel@tonic-gate 	}
4872*0Sstevel@tonic-gate 	/* Copy out the address */
4873*0Sstevel@tonic-gate 	mi_copyout(q, mp);
4874*0Sstevel@tonic-gate }
4875*0Sstevel@tonic-gate 
4876*0Sstevel@tonic-gate /*
4877*0Sstevel@tonic-gate  * Only allow MIB requests and M_FLUSHes to pass.
4878*0Sstevel@tonic-gate  * All other messages are nacked or dropped.
4879*0Sstevel@tonic-gate  */
4880*0Sstevel@tonic-gate static void
4881*0Sstevel@tonic-gate icmp_wput_restricted(queue_t *q, mblk_t *mp)
4882*0Sstevel@tonic-gate {
4883*0Sstevel@tonic-gate 	cred_t	*cr;
4884*0Sstevel@tonic-gate 	icmp_t	*icmp;
4885*0Sstevel@tonic-gate 
4886*0Sstevel@tonic-gate 	switch (DB_TYPE(mp)) {
4887*0Sstevel@tonic-gate 	case M_PROTO:
4888*0Sstevel@tonic-gate 	case M_PCPROTO:
4889*0Sstevel@tonic-gate 		if (MBLKL(mp) < sizeof (t_scalar_t)) {
4890*0Sstevel@tonic-gate 			freemsg(mp);
4891*0Sstevel@tonic-gate 			return;
4892*0Sstevel@tonic-gate 		}
4893*0Sstevel@tonic-gate 		icmp = (icmp_t *)q->q_ptr;
4894*0Sstevel@tonic-gate 		cr = DB_CREDDEF(mp, icmp->icmp_credp);
4895*0Sstevel@tonic-gate 
4896*0Sstevel@tonic-gate 		switch (((union T_primitives *)mp->b_rptr)->type) {
4897*0Sstevel@tonic-gate 		case T_SVR4_OPTMGMT_REQ:
4898*0Sstevel@tonic-gate 			if (!snmpcom_req(q, mp,
4899*0Sstevel@tonic-gate 			    icmp_snmp_set, icmp_snmp_get, cr))
4900*0Sstevel@tonic-gate 				(void) svr4_optcom_req(q, mp, cr,
4901*0Sstevel@tonic-gate 				    &icmp_opt_obj);
4902*0Sstevel@tonic-gate 			return;
4903*0Sstevel@tonic-gate 		case T_OPTMGMT_REQ:
4904*0Sstevel@tonic-gate 			(void) tpi_optcom_req(q, mp, cr, &icmp_opt_obj);
4905*0Sstevel@tonic-gate 			return;
4906*0Sstevel@tonic-gate 		default:
4907*0Sstevel@tonic-gate 			icmp_err_ack(q, mp, TSYSERR, ENOTSUP);
4908*0Sstevel@tonic-gate 			return;
4909*0Sstevel@tonic-gate 		}
4910*0Sstevel@tonic-gate 		/* NOTREACHED */
4911*0Sstevel@tonic-gate 	case M_IOCTL:
4912*0Sstevel@tonic-gate 		miocnak(q, mp, 0, ENOTSUP);
4913*0Sstevel@tonic-gate 		break;
4914*0Sstevel@tonic-gate 	case M_FLUSH:
4915*0Sstevel@tonic-gate 		putnext(q, mp);
4916*0Sstevel@tonic-gate 		break;
4917*0Sstevel@tonic-gate 	default:
4918*0Sstevel@tonic-gate 		freemsg(mp);
4919*0Sstevel@tonic-gate 		break;
4920*0Sstevel@tonic-gate 	}
4921*0Sstevel@tonic-gate }
4922*0Sstevel@tonic-gate 
4923*0Sstevel@tonic-gate static int
4924*0Sstevel@tonic-gate icmp_unitdata_opt_process(queue_t *q, mblk_t *mp, int *errorp,
4925*0Sstevel@tonic-gate     void *thisdg_attrs)
4926*0Sstevel@tonic-gate {
4927*0Sstevel@tonic-gate 	icmp_t	*icmp;
4928*0Sstevel@tonic-gate 	struct T_unitdata_req *udreqp;
4929*0Sstevel@tonic-gate 	int is_absreq_failure;
4930*0Sstevel@tonic-gate 	cred_t *cr;
4931*0Sstevel@tonic-gate 
4932*0Sstevel@tonic-gate 	icmp = (icmp_t *)q->q_ptr;
4933*0Sstevel@tonic-gate 
4934*0Sstevel@tonic-gate 	udreqp = (struct T_unitdata_req *)mp->b_rptr;
4935*0Sstevel@tonic-gate 	*errorp = 0;
4936*0Sstevel@tonic-gate 
4937*0Sstevel@tonic-gate 	cr = DB_CREDDEF(mp, icmp->icmp_credp);
4938*0Sstevel@tonic-gate 
4939*0Sstevel@tonic-gate 	*errorp = tpi_optcom_buf(q, mp, &udreqp->OPT_length,
4940*0Sstevel@tonic-gate 	    udreqp->OPT_offset, cr, &icmp_opt_obj,
4941*0Sstevel@tonic-gate 	    thisdg_attrs, &is_absreq_failure);
4942*0Sstevel@tonic-gate 
4943*0Sstevel@tonic-gate 	if (*errorp != 0) {
4944*0Sstevel@tonic-gate 		/*
4945*0Sstevel@tonic-gate 		 * Note: No special action needed in this
4946*0Sstevel@tonic-gate 		 * module for "is_absreq_failure"
4947*0Sstevel@tonic-gate 		 */
4948*0Sstevel@tonic-gate 		return (-1);		/* failure */
4949*0Sstevel@tonic-gate 	}
4950*0Sstevel@tonic-gate 	ASSERT(is_absreq_failure == 0);
4951*0Sstevel@tonic-gate 	return (0);	/* success */
4952*0Sstevel@tonic-gate }
4953*0Sstevel@tonic-gate 
4954*0Sstevel@tonic-gate void
4955*0Sstevel@tonic-gate icmp_ddi_init(void)
4956*0Sstevel@tonic-gate {
4957*0Sstevel@tonic-gate 	ICMP6_MAJ = ddi_name_to_major(ICMP6);
4958*0Sstevel@tonic-gate 	icmp_max_optsize =
4959*0Sstevel@tonic-gate 	    optcom_max_optsize(icmp_opt_obj.odb_opt_des_arr,
4960*0Sstevel@tonic-gate 		icmp_opt_obj.odb_opt_arr_cnt);
4961*0Sstevel@tonic-gate 
4962*0Sstevel@tonic-gate 	(void) icmp_param_register(icmp_param_arr, A_CNT(icmp_param_arr));
4963*0Sstevel@tonic-gate 
4964*0Sstevel@tonic-gate 	rawip_kstat_init();
4965*0Sstevel@tonic-gate }
4966*0Sstevel@tonic-gate 
4967*0Sstevel@tonic-gate void
4968*0Sstevel@tonic-gate icmp_ddi_destroy(void)
4969*0Sstevel@tonic-gate {
4970*0Sstevel@tonic-gate 	nd_free(&icmp_g_nd);
4971*0Sstevel@tonic-gate 
4972*0Sstevel@tonic-gate 	rawip_kstat_fini();
4973*0Sstevel@tonic-gate }
4974*0Sstevel@tonic-gate 
4975*0Sstevel@tonic-gate static void
4976*0Sstevel@tonic-gate rawip_kstat_init(void) {
4977*0Sstevel@tonic-gate 
4978*0Sstevel@tonic-gate 	rawip_named_kstat_t template = {
4979*0Sstevel@tonic-gate 		{ "inDatagrams",	KSTAT_DATA_UINT32, 0 },
4980*0Sstevel@tonic-gate 		{ "inCksumErrs",	KSTAT_DATA_UINT32, 0 },
4981*0Sstevel@tonic-gate 		{ "inErrors",		KSTAT_DATA_UINT32, 0 },
4982*0Sstevel@tonic-gate 		{ "outDatagrams",	KSTAT_DATA_UINT32, 0 },
4983*0Sstevel@tonic-gate 		{ "outErrors",		KSTAT_DATA_UINT32, 0 },
4984*0Sstevel@tonic-gate 	};
4985*0Sstevel@tonic-gate 
4986*0Sstevel@tonic-gate 	rawip_mibkp = kstat_create("icmp", 0, "rawip", "mib2",
4987*0Sstevel@tonic-gate 					KSTAT_TYPE_NAMED,
4988*0Sstevel@tonic-gate 					NUM_OF_FIELDS(rawip_named_kstat_t),
4989*0Sstevel@tonic-gate 					0);
4990*0Sstevel@tonic-gate 	if (rawip_mibkp == NULL)
4991*0Sstevel@tonic-gate 		return;
4992*0Sstevel@tonic-gate 
4993*0Sstevel@tonic-gate 	bcopy(&template, rawip_mibkp->ks_data, sizeof (template));
4994*0Sstevel@tonic-gate 
4995*0Sstevel@tonic-gate 	rawip_mibkp->ks_update = rawip_kstat_update;
4996*0Sstevel@tonic-gate 
4997*0Sstevel@tonic-gate 	kstat_install(rawip_mibkp);
4998*0Sstevel@tonic-gate }
4999*0Sstevel@tonic-gate 
5000*0Sstevel@tonic-gate static void
5001*0Sstevel@tonic-gate rawip_kstat_fini(void) {
5002*0Sstevel@tonic-gate 	if (rawip_mibkp) {
5003*0Sstevel@tonic-gate 		kstat_delete(rawip_mibkp);
5004*0Sstevel@tonic-gate 		rawip_mibkp = NULL;
5005*0Sstevel@tonic-gate 	}
5006*0Sstevel@tonic-gate }
5007*0Sstevel@tonic-gate 
5008*0Sstevel@tonic-gate static int
5009*0Sstevel@tonic-gate rawip_kstat_update(kstat_t *kp, int rw) {
5010*0Sstevel@tonic-gate 	rawip_named_kstat_t *rawipkp;
5011*0Sstevel@tonic-gate 
5012*0Sstevel@tonic-gate 	if ((kp == NULL) || (kp->ks_data == NULL))
5013*0Sstevel@tonic-gate 		return (EIO);
5014*0Sstevel@tonic-gate 
5015*0Sstevel@tonic-gate 	if (rw == KSTAT_WRITE)
5016*0Sstevel@tonic-gate 		return (EACCES);
5017*0Sstevel@tonic-gate 
5018*0Sstevel@tonic-gate 	rawipkp = (rawip_named_kstat_t *)kp->ks_data;
5019*0Sstevel@tonic-gate 
5020*0Sstevel@tonic-gate 	rawipkp->inDatagrams.value.ui32 =	rawip_mib.rawipInDatagrams;
5021*0Sstevel@tonic-gate 	rawipkp->inCksumErrs.value.ui32 =	rawip_mib.rawipInCksumErrs;
5022*0Sstevel@tonic-gate 	rawipkp->inErrors.value.ui32 =		rawip_mib.rawipInErrors;
5023*0Sstevel@tonic-gate 	rawipkp->outDatagrams.value.ui32 =	rawip_mib.rawipOutDatagrams;
5024*0Sstevel@tonic-gate 	rawipkp->outErrors.value.ui32 =		rawip_mib.rawipOutErrors;
5025*0Sstevel@tonic-gate 
5026*0Sstevel@tonic-gate 	return (0);
5027*0Sstevel@tonic-gate }
5028